繁体   English   中英

组合一个 Observables 列表并等待所有完成

[英]Combine a list of Observables and wait until all completed

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

我现有的代码使用 Bolts 来构建异步任务列表,并在执行其他步骤之前等待所有这些任务完成。 本质上,它构建了一个List<Task>并返回一个单独的Task ,当列表中的所有任务完成时,该Task被标记为已完成,如Bolts 站点上的示例所示

我正在寻找用RxJava替换Bolts并且我假设这种建立异步任务列表(大小事先未知)并将它们全部包装到单个Observable是可能的,但我不知道如何。

我试过查看mergezipconcat等......但无法开始处理我正在建立的List<Observable> ,因为它们似乎都适合一次只处理两个Observables如果我正确理解文档。

我正在尝试学习RxJava并且RxJava仍然很RxJava ,所以如果这是一个明显的问题或在某处的文档中进行了解释,请原谅我; 我试过搜索。 任何帮助将非常感激。

如果您有动态任务组合,您可以使用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 onComplete event
            //otherwise you will wait forever
            .toList()
            //could implement more intelligent logic. eg. check that everything is successful
            .map(results -> true);
}

另一个并行执行的好例子

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

听起来您正在寻找Zip 运算符

有几种不同的使用方式,让我们看一个例子。 假设我们有一些不同类型的简单 observables:

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 函数中,参数具有与被压缩的 observable 类型相对应的具体类型。

也可以直接压缩 observables 列表:

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[]参数,因为列表中的 observables 的类型以及它们的数量是未知的。 这意味着 zip 函数必须检查参数的数量并相应地转换它们。

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

编辑:使用 Zip 时,请确保被压缩的Observables都发出相同数量的项目。 在上面的示例中,所有三个 observable 都发出了一个项目。 如果我们把它们改成这样:

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永远不会被压缩,因为其他 observable 已经完成。

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

对于 Observables 的并行执行,另一个答案中提供的flatMap() 很好,但merge()会更直接。 请注意,merge 将在任何 Observable 出错时退出,如果您宁愿推迟退出直到所有 observable 完成,您应该查看mergeDelayError()

对于一对一,我认为应该使用Observable.concat() 静态方法 它的 javadoc 是这样写的:

concat(java.lang.Iterable>sequence) 将一个可观察对象的可迭代对象一个接一个地扁平化为一个可观察对象,而不将它们交错

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

此外,如果您只对完成任务感兴趣,而不对返回值感兴趣,那么您可能应该查看Completable而不是Observable

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

您可能看过与 2 个 Observable 一起使用的zip运算符。

还有静态方法Observable.zip 它有一种形式,应该对您有用:

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

您可以查看javadoc 了解更多信息。

使用 Kotlin

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

})

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

最后一个参数类型随着参数的数量而变化: BiFunction for 2 Function3 for 3 Function4 for 4 ...

我正在使用 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)
            }
        )
    }
}

我遇到了类似的问题,我需要从 rest 调用中获取搜索项,同时还需要集成来自 RecentSearchProvider.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) }
}

我从一组 observable 中创建了一个 observable,其中包含来自互联网的建议和结果列表,具体取决于查询。 之后,您只需使用 flatMapIterable 完成这些任务并使用 flatmap 运行它们,将结果放入数组中,稍后可以将其提取到回收视图中。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM