オブザーバブルのリストを組み合わせて、すべてが完了するまで待ちます


91

TL; DR どのように変換するためTask.whenAll(List<Task>)RxJava

私の既存のコードは、Boltsを使用して非同期タスクのリストを作成し、それらのタスクがすべて完了するまで待ってから、他の手順を実行します。基本的に、Boltsサイトの例のように、リスト内のすべてのタスクが完了するList<Task>と、を構築して、Task完了としてマークされたシングルを返します。

私は交換するために探しているBoltsRxJava、私は非同期タスクのリストを構築するこの方法を想定した(サイズは事前に知られていない)と、すべてのシングルにそれらをラップするよObservable可能ですが、私は方法がわかりません。

私は見てみましたmergezipconcat等...しかし、上で動作するように得ることができないList<Observable>、彼らはすべてのちょうど2つで作業に合わせたように見えるように私が構築するだろうとObservables私が正しくドキュメントを理解すれば一度に。

私は学ぼうとしていますがRxJava、まだ非常に新しいので、これが明らかな質問であるか、どこかのドキュメントで説明されている場合は、ご容赦ください。検索してみました。どんな助けでも大歓迎です。

回答:


73

Zip演算子を探しているようです。

使い方はいくつかありますので、例を見てみましょう。さまざまなタイプの単純なオブザーバブルがいくつかあるとします。

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関数では、パラメーターには、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));

ただし、どちらの場合も、Object[]リスト内のオブザーバブルのタイプとその数が事前にわからないため、zip関数は単一のパラメーターのみを受け入れることができます。これは、zip関数がパラメーターの数をチェックし、それに応じてそれらをキャストする必要があることを意味します。

とにかく、上記の例はすべて最終的には印刷されます 1 Blah true

編集: Zipを使用する場合はObservables、zipするすべてのアイテムが同じ数のアイテムを放出することを確認してください。上記の例では、3つのオブザーバブルすべてが1つのアイテムを放出しました。それらを次のようなものに変更する場合:

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

次に、zip関数に渡されるアイテムは1, Blah, Trueand2, Hello, Trueだけです。3他のオブザーバブルが完了したため、アイテムが圧縮されることはありません。


9
いずれかの呼び出しが失敗した場合、これは機能しません。その場合、すべての通話が失われます。
starWind0 2016年

1
@ StarWind0を使用するとonErrorResumeNext、エラーをスキップできます。例:Observable.zip(ob1, ob2........).onErrorResumeNext(Observable.<String>empty())
vuhung3990 2018年

100個のオブザーバブルがある場合はどうなりますか?
KrzysztofKubicki19年

79

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);
}

並列実行のもう1つの良い例

注:エラー処理の要件はよくわかりません。たとえば、1つのタスクだけが失敗した場合の対処方法。このシナリオを確認する必要があると思います。


16
これは、質問に「リスト内のすべてのタスクが完了したとき」と記載されていることを考慮すると、受け入れられる回答である必要があります。zipタスクの1つが完了するとすぐに完了を通知するため、適用されません。
user3707125 2016

1
@MyDogTom:Java7構文(ラムダではない)バージョンで回答を更新できますか?
sanedroid 2017年

3
@PoojaGaikwadラムダを使用すると、より読みやすくなります。最初のラムダをにnew Func1<Observable<Boolean>, Observable<Boolean>>()...、2番目のラムダをに置き換えるだけですnew Func1<List<Boolean>, Boolean>()
MyDogTom 2017年

@soshial RxJava 2は、これまでええ、RxJavaで起こった最悪です
egorikem

15

提案された提案のうち、zip()は実際に観察可能な結果を​​互いに組み合わせます。これは、必要なものである場合とそうでない場合がありますが、質問では尋ねられませんでした。質問では、必要なのは、各操作を1つずつまたは並列に実行することだけでした(指定されていませんが、リンクされたBoltsの例は並列実行に関するものでした)。また、zip()は、オブザーバブルのいずれかが完了するとすぐに完了するため、要件に違反しています。

Observablesの並列実行の場合、他の回答に示されているflatMap()は問題ありませんが、merge()の方が簡単です。いずれかのオブザーバブルのエラーでマージが終了することに注意してください。すべてのオブザーバブルが終了するまで終了を延期する場合は、mergeDelayError()確認する必要があります。

1つずつ、Observable.concat()静的メソッドを使用する必要があると思います。そのjavadocは次のように述べています。

concat(java.lang.Iterable> sequence)オブザーバブルのイテラブルを、インターリーブせずに1つのオブザーバブルに次々にフラット化します

これは、並列実行を望まない場合に求めているもののように聞こえます。

また、戻り値ではなく、タスクの完了のみに関心がある場合は、ObservableではなくCompletableを調べる必要があります。

TLDR:タスクを1つずつ実行し、タスクが完了したときに完了イベントを実行するには、Completable.concat()が最適だと思います。並列実行の場合、Completable.merge()またはCompletable.mergeDelayError()は解決策のように聞こえます。前者は、完了可能なエラーが発生するとすぐに停止し、後者は、エラーが発生した場合でもすべてを実行し、エラーを報告します。


2

あなたはおそらくzip2つのObservablesで動作する演算子を見ました。

静的メソッドもありますObservable.zip。それはあなたに役立つはずの1つの形式を持っています:

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

詳細については、javadocを確認してください


2

Kotlinを使用

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

})

関数の引数の型を設定することが重要です。そうしないと、コンパイルエラーが発生します。

最後の引数タイプは引数の数によって変わります:BiFunction for 2 Function3 for 3 Function4 for 4 .. ..


1

私は、JavaRxObservablesとRxKotlinを使用してKotlinで計算ヒーブコードを書いています。完了すべきオブザーバブルのリストを観察し、その間に進捗状況と最新の結果を更新してもらいたいと思います。最後に、最良の計算結果を返します。追加の要件は、すべてのCPUコアを使用するためにObservablesを並行して実行することでした。私はこの解決策に行き着きました:

@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)
            }
        )
    }
}

RxKotlinまたは@Volatileに精通していませんが、これが複数のスレッドによって同時に呼び出されている場合、これはどのように機能しますか?結果はどうなりますか?
eis 2018

0

同様の問題がありました。残りの呼び出しから検索アイテムをフェッチすると同時に、RecentSearchProvider.AUTHORITYから保存された提案を統合し、それらを1つの統合リストに結合する必要がありました。@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を使用して実行し、結果を配列に配置します。これは、後でリサイクルビューにフェッチできます。


0

Project Reactorを使用する場合は、を使用できますMono.when

Mono.when(publisher1, publisher2)
.map(i-> {
    System.out.println("everything is done!");
    return i;
}).block()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.