簡體   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