web-dev-qa-db-ja.com

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

TL; DRTask.whenAll(List<Task>)RxJavaに変換するには?

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

BoltsRxJavaで置き換えたいと考えており、非同期タスクのリスト(サイズは事前に知られていない)を構築し、それらをすべて単一のObservableにラップするこの方法を想定していますが、方法はわかりません。

mergeZipconcatなどを見てみましたが、理解していれば、一度に2つだけのObservablesを処理するように調整されているように見えるので、構築するList<Observable>を処理できません正しくドキュメント。

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

72
Craig Russell

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関数では、パラメーターには、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[]パラメーターのみを受け入れることができます。これは、Zip関数がパラメーターの数をチェックし、それに応じてキャストする必要があることを意味します。

とにかく、上記の例はすべて最終的に1 Blah trueを出力します

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

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関数に渡される唯一のアイテムになります。他のオブザーバブルが完了したため、アイテム3wは決して圧縮されません。

56
Malt

動的なタスク構成がある場合は、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つのタスクのみが失敗した場合の対処方法。このシナリオを確認する必要があると思います。

69
MyDogTom

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

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

1つずつ、 Observable.concat()static method を使用する必要があると思います。 javadocの状態は次のとおりです。

concat(Java.lang.Iterable> sequences)ObservableのIterableを、インターリーブせずに次々とObservableにフラット化します

並列実行が必要ない場合は、後のように聞こえます。

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

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

9
eis

コリンと

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

})

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

最後の引数のタイプは、引数の数とともに変化します。2つの関数の3つの関数3の関数4の関数4.

3
Kevin ABRIOUX

おそらく、2つのObservableで機能するZip演算子を見ました。

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

Zip(Java.lang.Iterable<? extends Observable<?>> ws, FuncN<? extends R> zipFunction)

詳細はjavadoc を確認できます。

1
LordRaydenMK

JavaRx Observablesと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)
            }
        )
    }
}
1
Sjors