【问题标题】:How To Do Recursive Observable Call in RxJava?如何在 RxJava 中进行递归 Observable 调用?
【发布时间】:2015-07-07 16:37:15
【问题描述】:

我对@9​​87654321@(以及一般的响应式范式)很陌生,所以请多多包涵。

假设我有这个News 和这个嵌套的Comment 数据结构:

public class News {
  public int id;
  public int[] commentIds; //only top level comments
  public News(int id, int[] commentIds) {
    this.id = id;
    this.commentIds = commentIds;
  }
}

public class Comment {
  public int id;
  public int parentId; //ID of parent News or parent comment
  public int[] childIds;
  public Comment(int id, int parentId, int[] childIds) {
    this.id = id;
    this.parentId = parentId;
    this.childIds = childIds;
  }
}

假设我有这个 API 端点:

getComments(int commentId) //return Observable<Comment> for Comment with ID commentId

现在,让我们假设:

getComments(1); //will return Comment(1, 99, [3,4])
getComments(2); //will return Comment(2, 99, [5,6])
getComments(3); //will return Comment(3, 1, [])
getComments(4); //will return Comment(4, 1, [])
getComments(5); //will return Comment(5, 2, [])
getComments(6); //will return Comment(6, 2, [])

**

现在,如果我有News n = News(99, [1,2]),我如何递归地获取它的所有子评论?即获取 ID 为 [1,2,3,4,5,6] 的 cmets?

**

我搜索并偶然发现了这个:https://jkschneider.github.io/blog/2014/recursive-observables-with-rxjava.html

这是递归函数:

public class FileRecursion {
    static Observable<File> listFiles(File f) {
        if(f.isDirectory())
            return Observable.from(f.listFiles()).flatMap(FileRecursion::listFiles);
        return Observable.just(f);
    }

    public static void main(String[] args) {
          Observable.just(new File("/Users/joschneider/Desktop"))
                  .flatMap(FileRecursion::listFiles)
                  .subscribe(f -> System.out.println(f.getAbsolutePath()));
    }
}

它展示了如何进行递归 observable 调用的示例,但内部函数 (f.listFiles()) 是一个阻塞操作(不返回另一个 Observable)。在我的例子中,内部函数 (getComments) 是一个非阻塞函数,它返回另一个 Observables。我该怎么做?

任何帮助将不胜感激。

【问题讨论】:

  • 你错了 - listFiles 确实返回了 Observable
  • 嘿,你是对的!我怎么可能没有意识到这一点?好的,那我该如何实现这个......嗯......
  • 哦,我的意思是 内部函数 f.listFiles(),它不会返回可观察值,对吧?返回 observable 的是FileRecursion::listFiles。还是我在这里错过了什么?
  • 你是对的——那个是File#listFiles,它返回File[]

标签: java reactive-programming rx-java observable


【解决方案1】:

这实际上与文章中描述的相同:

Observable<Comment> getInnerComments(Comment comment) {
    if (comment.childIds.length > 0)
        return Observable.merge(
                Observable.just(comment),
                Observable.from(comment.childIds)
                        .flatMap(id -> getComments(id))
                        .flatMap(this::getInnerComments));
    return Observable.just(comment);
}

public static void main(String[] args) {
    getComments(1)
          .flatMap(this::getInnerComments)
          .subscribe(c -> System.out.println(comment.toString()));
}

我从 id = 1 的评论开始,然后将其传递给 getInnerComments()getInnerComments() 检查评论是否有孩子。如果是这样,它会遍历每个子 id (Observable#from) 并使用您的 getComments(int) API 加载每个子。然后将每个孩子传递给getInnerComments() 以执行相同的过程。如果评论没有子评论,则立即使用Observable#just 返回。

这是伪代码,未经测试,但你应该明白。


下面是一个示例,说明如何获取所有 cmets,然后将它们聚合到一个 List&lt;Comment&gt;

getNews(99)
        .flatMap(news -> Observable.from(news.commentIds))
        .flatMap(commentId -> getComments(commentId))
        .flatMap(comment -> getInnerComments(comment))
        .toList()
        .subscribe(commentList -> { });

【讨论】:

  • Egor,那个递归只给了我 leaf 评论(即没有任何孩子的评论)。看起来非叶子评论在递归函数中被订阅了。任何想法?等等,我没有看到你更新的答案。让我先考虑一下。
  • 嗯,你说得对,我没有想到。我编辑了我的答案 - 现在 getInnerComments() 返回传递给它的评论,然后做其他事情。
  • @HoverPhoenix,等一下,我第一次看到你的评论时没看懂。让我打开我的 IDE 并尝试一些东西。
  • 你太棒了,叶戈尔。有效!我缺少的关键点是flatMapflatMap 中。而且,现在我明白了为什么需要运算符 MERGE 了!谢谢!我已接受您的帖子作为答案。
  • 是的,我刚刚测试过它,它工作正常。很高兴我能帮助你!。
猜你喜欢
  • 2018-11-02
  • 1970-01-01
  • 2016-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多