合并一个Observable列表,等待所有完成

时间:2016-02-12 08:28:35

标签: java rx-java reactive-programming rx-android

TL; DR 如何将Task.whenAll(List<Task>)转换为RxJava

我现有的代码使用Bolts构建异步任务列表,并等待所有这些任务完成后再执行其他步骤。从本质上讲,它会构建一个List<Task>并返回一个Task,根据example on the Bolts site,当列表中的所有任务完成时,标记为已完成。{ / p>

我希望将Bolts替换为RxJava,而我假设这种方法可以构建一个异步任务列表(大小未提前知道)并将它们全部包装起来单个Observable是可能的,但我不知道如何。

我已尝试查看mergezipconcat等...但无法继续使用List<Observable>我{如果我理解正确的文档,那么他们似乎只需要同时处理两个Observables

我试图学习RxJava并且仍然是新手,所以请原谅我,如果这是一个明显的问题或在某个地方的文档中解释过;我试过搜索。任何帮助将不胜感激。

8 个答案:

答案 0 :(得分:68)

如果您拥有动态任务合成,则可以使用flatMap。像这样:

public Observable<Boolean> whenAll(List<Observable<Boolean>> tasks) {
    return Observable.from(tasks)
            //execute in parallel
            .flatMap(task -> task.observeOn(Schedulers.computation()))
            //wait, until all task are executed
            //be aware, all your observable should emit onComplemete event
            //otherwise you will wait forever
            .toList()
            //could implement more intelligent logic. eg. check that everything is successful
            .map(results -> true);
}

Another good example of parallel execution

注意:我真的不知道您对错误处理的要求。例如,如果只有一个任务失败,该怎么办。我认为你应该验证这种情况。

答案 1 :(得分:54)

听起来你正在寻找Zip operator

有几种不同的使用方法,让我们看一个例子。假设我们有一些不同类型的简单可观察量:

Observable<Integer> obs1 = Observable.just(1);
Observable<String> obs2 = Observable.just("Blah");
Observable<Boolean> obs3 = Observable.just(true);

等待它们的最简单方法是这样的:

Observable.zip(obs1, obs2, obs3, (Integer i, String s, Boolean b) -> i + " " + s + " " + b)
.subscribe(str -> System.out.println(str));

请注意,在zip函数中,参数具有与要压缩的可观察对象类型相对应的具体类型。

也可以直接压缩可观察列表:

List<Observable<?>> obsList = Arrays.asList(obs1, obs2, obs3);

Observable.zip(obsList, (i) -> i[0] + " " + i[1] + " " + i[2])
.subscribe(str -> System.out.println(str));

...或将列表包装到Observable<Observable<?>>

Observable<Observable<?>> obsObs = Observable.from(obsList);

Observable.zip(obsObs, (i) -> i[0] + " " + i[1] + " " + i[2])
.subscribe(str -> System.out.println(str));

但是,在这两种情况下,zip函数只能接受单个Object[]参数,因为列表中的observable类型事先不知道,也不知道它们的编号。这意味着zip函数必须检查参数的数量并相应地转换它们。

无论如何,上述所有示例最终都会打印1 Blah true

编辑:使用Zip时,请确保所有正在压缩的Observables发出相同数量的项目。在上面的例子中,所有三个可观察者都发出了一个项目。如果我们将它们改为这样的话:

Observable<Integer> obs1 = Observable.from(new Integer[]{1,2,3}); //Emits three items
Observable<String> obs2 = Observable.from(new String[]{"Blah","Hello"}); //Emits two items
Observable<Boolean> obs3 = Observable.from(new Boolean[]{true,true}); //Emits two items

然后1, Blah, True2, Hello, True将成为传递给zip函数的唯一项目。由于其他可观测量已完成,因此永远不会压缩项目3

答案 2 :(得分:8)

在提出的建议中,zip()实际上彼此结合了可观察到的结果,可能是(也可能不是)想要的结果,但未在问题中提出。在这个问题中,所需要的只是每个操作的执行,要么是一对一或并行执行(未指定,但是链接的Bolts示例是关于并行执行的)。另外,当任何可观察对象完成时,zip()将立即完成,因此违反了要求。

对于Observables的并行执行,flatMap()presented in the other answer很好,但是merge()会更直接。请注意,合并将在任何一个Observable发生错误时退出,如果您宁愿将退出推迟到所有Observable完成之前,则应该查看mergeDelayError()

我认为应该Observable.concat() static method一一使用。其javadoc状态如下:

  

concat(java.lang.Iterable>序列)   将可迭代的可观测变量平整化为一个可观测变量,一个又一个,而不进行交织

如果您不想并行执行,这听起来像您想要的。

此外,如果您仅对完成任务感兴趣,而不对返回值感兴趣,则应该考虑使用Completable而不是Observable

TLDR:对于任务和oncompletion事件完成时的一对一执行,我认为Completable.concat()最适合。对于并行执行,Completable.merge()或Completable.mergeDelayError()听起来像解决方案。前者将立即停止对任何可完成对象的任何错误,而后者将即使它们中的一个有错误也将全部执行,然后才报告错误。

答案 3 :(得分:3)

用Kolin

Observable.zip(obs1, obs2, BiFunction { t1 : Boolean, t2:Boolean ->

})

设置函数参数的类型很重要,否则会出现编译错误

最后一个参数类型随参数的数量而变化: BiFunction为2 功能3为3 功能4为4 ...

答案 4 :(得分:1)

您可能已经查看了使用2个Observables的zip运算符。

还有静态方法Observable.zip。它有一个对你有用的形式:

zip(java.lang.Iterable<? extends Observable<?>> ws, FuncN<? extends R> zipFunction)

您可以查看javadoc for more.

答案 5 :(得分:1)

我正在使用JavaRx Observables和RxKotlin在Kotlin中编写一些计算升沉代码。我想观察要完成的可观察列表,同时向我提供有关进度和最新结果的更新。最后它返回最佳计算结果。额外的要求是并行运行Observables以使用我所有的cpu核心。我最终得到了这个解决方案:

@Volatile var results: MutableList<CalculationResult> = mutableListOf()

fun doALotOfCalculations(listOfCalculations: List<Calculation>): Observable<Pair<String, CalculationResult>> {

    return Observable.create { subscriber ->
        Observable.concatEager(listOfCalculations.map { calculation: Calculation ->
            doCalculation(calculation).subscribeOn(Schedulers.computation()) // function doCalculation returns an Observable with only one result
        }).subscribeBy(
            onNext = {
                results.add(it)
                subscriber.onNext(Pair("A calculation is ready", it))

            },
            onComplete = {
                subscriber.onNext(Pair("Finished: ${results.size}", findBestCalculation(results)) 
                subscriber.onComplete()
            },
            onError = {
                subscriber.onError(it)
            }
        )
    }
}

答案 6 :(得分:0)

我有类似的问题,我需要从rest调用中获取搜索项目,同时还需要整合来自NearestSearchProvider.AUTHORITY的保存建议,并将它们组合到一个统一的列表中。我试图使用@MyDogTom解决方案,很遗憾,RxJava中没有Observable.from。经过一番研究,我找到了适合我的解决方案。

 fun getSearchedResultsSuggestions(context : Context, query : String) : Single<ArrayList<ArrayList<SearchItem>>>
{
    val fetchedItems = ArrayList<Observable<ArrayList<SearchItem>>>(0)
    fetchedItems.add(fetchSearchSuggestions(context,query).toObservable())
    fetchedItems.add(getSearchResults(query).toObservable())

    return Observable.fromArray(fetchedItems)
        .flatMapIterable { data->data }
        .flatMap {task -> task.observeOn(Schedulers.io())}
        .toList()
        .map { ArrayList(it) }
}

我从可观察对象数组中创建了一个可观察对象,其中包含根据查询来自互联网的建议和结果列表。之后,您只需使用flatMapIterable查看这些任务,然后使用flatmap运行它们,然后将结果放置在数组中,然后可以将其提取到回收视图中。

答案 7 :(得分:0)

如果您使用Project Reactor,则可以使用Mono.when

Mono.when(publisher1, publisher2)
.map(i-> {
    System.out.println("everything is done!");
    return i;
}).block()