web-dev-qa-db-ja.com

RxJava:依存関係を持つ複数のObservableを構成し、最後にすべての結果を収集する方法

私はRxJavaを学び、最初の実験として、最初のrun()メソッドのコードを this codeNetflixのブログ で引用)で書き直そうとしています。 RxJavaは、RxJavaを使用して非同期性を改善するための問題として解決できます。つまり、最初のFuture(f1.get())の結果を待たずに残りのコードに進むことができます。

f3f1に依存します。 flatMapがこれを処理するようです。

Observable<String> f3Observable = Observable.from(executor.submit(new CallToRemoteServiceA()))
    .flatMap(new Func1<String, Observable<String>>() {
        @Override
        public Observable<String> call(String s) {
            return Observable.from(executor.submit(new CallToRemoteServiceC(s)));
        }
    });

次に、f4f5f2に依存します。私はこれを持っています:

final Observable<Integer> f4And5Observable = Observable.from(executor.submit(new CallToRemoteServiceB()))
    .flatMap(new Func1<Integer, Observable<Integer>>() {
        @Override
        public Observable<Integer> call(Integer i) {
            Observable<Integer> f4Observable = Observable.from(executor.submit(new CallToRemoteServiceD(i)));
            Observable<Integer> f5Observable = Observable.from(executor.submit(new CallToRemoteServiceE(i)));
            return Observable.merge(f4Observable, f5Observable);
        }
    });

それは奇妙になり始めます(mergeingはおそらく私が望んでいることではありません...)が、最後にこれを行うことができますが、私が望んでいることではありません:

f3Observable.subscribe(new Action1<String>() {
    @Override
    public void call(String s) {
        System.out.println("Observed from f3: " + s);
        f4And5Observable.subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer i) {
                System.out.println("Observed from f4 and f5: " + i);
            }
        });
    }
});

それは私に与えます:

Observed from f3: responseB_responseA
Observed from f4 and f5: 140
Observed from f4 and f5: 5100

これはすべての数字ですが、残念ながら個別の呼び出しで結果が得られるため、元のコードの最終的なprintlnを置き換えることはできません。

System.out.println(f3.get() + " => " + (f4.get() * f5.get()));

同じ行でこれら両方の戻り値にアクセスする方法がわかりません。私はここに欠けている機能的なプログラミングのフーがおそらくあると思います。これどうやってするの?ありがとう。

20
Steve Kehlet

本当に必要なのは、RXの使用方法に関するもう少しの励ましと見方だけです。ドキュメンテーションと大理石の図をもっと読むことをお勧めします(それらは常に役に立つとは限りません)。 lift()関数と演算子を調べることもお勧めします。

  • オブザーバブルのポイントは、データフローとデータ操作を単一のオブジェクトに連結することです。
  • mapflatMap、およびfilterへの呼び出しのポイントは、データフロー内のデータを操作することです。
  • マージのポイントは、データフローを結合することです
  • オペレーターのポイントは、オブザーバブルの安定した流れを中断させ、データフローで独自の操作を定義できるようにすることです。たとえば、移動平均演算子をコーディングしました。これは、オブザーバブルのdoubleのndoublesを合計して、移動平均のストリームを返します。コードは文字通りこのように見えました

    Observable movingAverage = Observable.from(mDoublesArray).lift(new MovingAverageOperator(frameSize))

当然のことながら、多くのフィルタリングメソッドのすべてが内部でlift()を持っていることに安心します。

とは言うものの;複数の依存関係をマージするために必要なことは次のとおりです。

  • mapまたはflatMapを使用して、すべての受信データを標準データ型に変更する
  • 標準データ型をストリームにマージする
  • あるオブジェクトが別のオブジェクトで待機する必要がある場合、またはストリーム内のデータを順序付ける必要がある場合は、カスタム演算子を使用します。注意:このアプローチはストリームを遅くします
  • すべてのデータを収集するためにリストまたはサブスクライブするために使用
19
user3407713

編集:誰かが質問の編集として追加した次のテキストを回答に変換しました。適切なSOすること、しかしこれは明らかに正しい方法ではないのでこれを答えとは思わない。このコードを使用することも、コピーすることもお勧めしません他の/より良い解決策とコメントを歓迎します!


これを次の方法で解決できました。 flatMapオブザーバブルを複数回使用できることに気付きませんでした。結果は1回しか使用できないと想定しました。だから私はflatMap f2Observableを2回(申し訳ありませんが、元の投稿からコードの一部の名前を変更しました)、すべてのObservablesでZipし、サブスクライブします。 Map内のZipが値を集計することは、型のジャグリングのために望ましくありません。 その他の/より良いソリューションとコメントを歓迎します!完全なコードは要点で表示可能 。ありがとうございました。

Future<Integer> f2 = executor.submit(new CallToRemoteServiceB());
Observable<Integer> f2Observable = Observable.from(f2);
Observable<Integer> f4Observable = f2Observable
    .flatMap(new Func1<Integer, Observable<Integer>>() {
        @Override
        public Observable<Integer> call(Integer integer) {
            System.out.println("Observed from f2: " + integer);
            Future<Integer> f4 = executor.submit(new CallToRemoteServiceD(integer));
            return Observable.from(f4);
        }       
    });     

Observable<Integer> f5Observable = f2Observable
    .flatMap(new Func1<Integer, Observable<Integer>>() {
        @Override
        public Observable<Integer> call(Integer integer) {
            System.out.println("Observed from f2: " + integer);
            Future<Integer> f5 = executor.submit(new CallToRemoteServiceE(integer));
            return Observable.from(f5);
        }       
    });     

Observable.Zip(f3Observable, f4Observable, f5Observable, new Func3<String, Integer, Integer, Map<String, String>>() {
    @Override
    public Map<String, String> call(String s, Integer integer, Integer integer2) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("f3", s);
        map.put("f4", String.valueOf(integer));
        map.put("f5", String.valueOf(integer2));
        return map;
    }       
}).subscribe(new Action1<Map<String, String>>() {
    @Override
    public void call(Map<String, String> map) {
        System.out.println(map.get("f3") + " => " + (Integer.valueOf(map.get("f4")) * Integer.valueOf(map.get("f5"))));
    }       
});     

そして、これは私に望ましい出力をもたらします:

responseB_responseA => 714000
7
Steve Kehlet

あなたが探しているのはスイッチマップだと思います。 APIから新しいセッションを取得するセッションサービスがあり、さらにデータを取得する前にそのセッションが必要になるという同様の問題が発生しました。データコールで使用するためにsessionTokenを返すObservableセッションに追加できます。

getSessionはオブザーバブルを返します。

public getSession(): Observable<any>{
  if (this.sessionToken)
    return Observable.of(this.sessionToken);
  else if(this.sessionObservable)
    return this.sessionObservable;
  else {
    // simulate http call 
    this.sessionObservable = Observable.of(this.sessonTokenResponse)
    .map(res => {
      this.sessionObservable = null;
      return res.headers["X-Session-Token"];
    })
    .delay(500)
    .share();
    return this.sessionObservable;
  }
}

getDataはそのオブザーバブルを取得して追加します。

public getData() {
  if (this.dataObservable)
    return this.dataObservable;
  else {
    this.dataObservable = this.sessionService.getSession()
      .switchMap((sessionToken:string, index:number) =>{
        //simulate data http call that needed sessionToken
          return Observable.of(this.dataResponse)
          .map(res => {
            this.dataObservable = null;
            return res.body;
          })
          .delay(1200)
        })
        .map ( data => {
          return data;
        })
        .catch(err => {
          console.log("err in data service", err);
         // return err;
        })
        .share();
    return this.dataObservable;
  }
}

依存関係のないオブザーバブルを結合するには、引き続きフラットマップが必要です。

Plunkr: http://plnkr.co/edit/hiA1jP?p=info

スイッチマップを使用するアイデアを得た場所: http://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html

2
Stephen