あるObservableシーケンスを別のシーケンスが完了するのを待ってから放出する方法は?


93

私が持っているObservableと言う、のように:

var one = someObservable.take(1);

one.subscribe(function(){ /* do something */ });

次に、私は秒がありObservableます:

var two = someOtherObservable.take(1);

今、私がしたいsubscribe()two、私はそれを確認するone前に完了しているtwo加入者が発射されます。

two最初のバッファリングが完了するのを2番目のバッファリング方法で待機させるには、どのようなバッファリング方法を使用できますか?

完了するtwoまで一時停止したいと思いますone


1
これに対する答えは.exhaustMap()メソッドだと思いますが、実装方法を知っているふりはしません-詳細な説明はこちら:blog.angular-university.io/rxjs-higher-order-mapping
Peter Nixey

回答:


56

私が考えることができるいくつかの方法

import {take, publish} from 'rxjs/operators'
import {concat} from 'rxjs'

//Method one

var one = someObservable.pipe(take(1));
var two = someOtherObservable.pipe(take(1));
concat(one, two).subscribe(function() {/*do something */});

//Method two, if they need to be separate for some reason
var one = someObservable.pipe(take(1));
var two = someOtherObservable.pipe(take(1), publish());
two.subscribe(function(){/*do something */});
one.subscribe(function(){/*do something */}, null, two.connect.bind(two));

1
pauseとのresume代わりにpublishとを使用することになりましたconnectが、例2は基本的に私がたどったルートです。
スティーブン

1
このメソッドは、subscribe()関数内のone2番目の(two)の前に最初のobservable()を常に解決しますか?
ジョン

使ってみませんObservable.forkJoin()か?このリンクを参照してくださいlearnrxjs.io/operators/combination/forkjoin.htmlを
mspasiuk

18
OPの要件に従って@mspasiukは、最初の要件が完了した後に2番目のサブスクライブのみを望んでいました。forkJoin同時にサブスクライブします。
paulpdaniels 2017

18

実行順序が保持されていることを確認したい場合は、次の例としてflatMapを使用できます。

const first = Rx.Observable.of(1).delay(1000).do(i => console.log(i));
const second = Rx.Observable.of(11).delay(500).do(i => console.log(i));
const third = Rx.Observable.of(111).do(i => console.log(i));

first
  .flatMap(() => second)
  .flatMap(() => third)
  .subscribe(()=> console.log('finished'));

結果は次のようになります。

"1"
"11"
"111"
"finished"

16

skipUntil()とlast()

skipUntil:別のオブザーバブルが放出されるまで放出されたアイテムを無視します

last:シーケンスから最後の値を発行します(つまり、完了するまで待ってから発行します)

に渡されたobservableから放出されたものはすべてskipUntilスキップをキャンセルすることに注意してください。そのlast()ため、ストリームが完了するのを待つために-を追加する必要があります。

main$.skipUntil(sequence2$.pipe(last()))

公式:https//rxjs-dev.firebaseapp.com/api/operators/skipUntil


考えられる問題:何も出力されない場合last()、それ自体でエラーが発生することに注意してください。last()オペレータは持っていませんdefaultパラメータをしかし、述語と組み合わせて使用する場合にのみ。この状況があなたにとって問題である場合(sequence2$放出せずに完了する可能性がある場合)、これらのいずれかが機能するはずです(現在テストされていません):

main$.skipUntil(sequence2$.pipe(defaultIfEmpty(undefined), last()))
main$.skipUntil(sequence2$.pipe(last(), catchError(() => of(undefined))

これundefinedは発行される有効なアイテムですが、実際には任意の値である可能性があることに注意してください。また、これは接続されsequence2$ているmain$パイプであり、パイプではないことに注意してください。


非常に不器用なデモ:angular-vgznak.stackblitz.ioあなたは、コンソールトレイを開くためにクリックする必要がある
Simon_Weaver

構文が間違っています。skipUntilをオブザーバブルに直接アタッチすることはできません。そうしないと、次のエラーが発生します。 'プロパティ' skipUntil 'はタイプ' Observable <any> 'に存在しません。最初に.pipe()を実行する必要があります
London8 0420

はい、これはパイプが必要になる前の古い答えです。言及してくれてありがとう。今すぐ更新しますが、電話中です。答えを自由に編集してください。
Simon_Weaver

13

switchMapの結果セレクターを利用するさらに別の可能性があります

var one$ = someObservable.take(1);
var two$ = someOtherObservable.take(1);
two$.switchMap(
    /** Wait for first Observable */
    () => one$,
    /** Only return the value we're actually interested in */
    (value2, value1) => value2
  )
  .subscribe((value2) => {
    /* do something */ 
  });

switchMapの結果セレクターが減価償却されたため、ここに更新されたバージョンがあります

const one$ = someObservable.pipe(take(1));
const two$ = someOtherObservable.pipe(
  take(1),
  switchMap(value2 => one$.map(_ => value2))
);
two$.subscribe(value2 => {
  /* do something */ 
});

8

これを再利用できる方法は次のとおりです(タイプスクリプトですが、jsに適合させることができます)。

export function waitFor<T>(signal: Observable<any>) {
    return (source: Observable<T>) =>
        new Observable<T>(observer =>
            signal.pipe(first())
                .subscribe(_ =>
                    source.subscribe(observer)
                )
        );
}

そして、他の演算子と同じように使用できます。

var two = someOtherObservable.pipe(waitFor(one), take(1));

これは基本的に、監視可能なシグナルが最初のイベントを発行するまで、監視可能なソースでサブスクライブを延期する演算子です。


この再利用可能な関数のrxswiftバージョンはありますか
sujith14 0620

6

第二に観察可能である場合は、ホット、そこにある別の方法で行うために一時停止/再開は

var pauser = new Rx.Subject();
var source1 = Rx.Observable.interval(1000).take(1);
/* create source and pause */
var source2 = Rx.Observable.interval(1000).pausable(pauser);

source1.doOnCompleted(function () { 
  /* resume paused source2 */ 
  pauser.onNext(true);
}).subscribe(function(){
  // do something
});

source2.subscribe(function(){
  // start to recieve data 
});

また、バッファリングされたバージョンpausableBufferedを使用して、一時停止がオンになっている間もデータを保持できます。


2

これはさらに別の方法ですが、私はより直接的で直感的な(または、Promisesに慣れている場合は少なくとも自然な)アプローチを感じます。基本的に、Observable.create()ラップにoneを使用しtwoて単一のObservableとしてObservableを作成します。これは、動作方法と非常によく似ていますPromise.all()

var first = someObservable.take(1);
var second = Observable.create((observer) => {
  return first.subscribe(
    function onNext(value) {
      /* do something with value like: */
      // observer.next(value);
    },
    function onError(error) {
      observer.error(error);
    },
    function onComplete() {
      someOtherObservable.take(1).subscribe(
        function onNext(value) {
          observer.next(value);
        },
        function onError(error) {
          observer.error(error);
        },
        function onComplete() {
          observer.complete();
        }
      );
    }
  );
});

それで、ここで何が起こっているのですか?まず、新しいObservableを作成します。に渡される関数はObservable.create()、適切な名前onSubscriptionで、オブザーバー(渡されるパラメーターから構築されますsubscribe())に渡されます。これは、新しいPromiseを作成するときに、1つのオブジェクトに類似しresolvereject結合されます。これが私たちが魔法を機能させる方法です。

ではonSubscription、最初のObservableをサブスクライブします(上記の例では、これはと呼ばれていましたone)。どのように我々はハンドルnexterrorあなた次第ですが、私のサンプルで提供されるデフォルトは、一般的に適切でなければなりません。ただし、completeイベントを受信すると、つまりこれでone完了したら、次のObservableをサブスクライブできます。これにより、最初のObservableが完了した後、2番目のObservableを起動します。

2番目のObservableに提供されているオブザーバーの例はかなり単純です。基本的に、secondtwoではOPで期待するように動作します。より具体的にsecondは、someOtherObservable(のためにtake(1))によって発行された最初で唯一の最初の値を発行し、エラーがないと仮定して完了します。

これは、私の例が実際に機能していることを確認したい場合にコピー/貼り付けできる完全な実例です。

var someObservable = Observable.from([1, 2, 3, 4, 5]);
var someOtherObservable = Observable.from([6, 7, 8, 9]);

var first = someObservable.take(1);
var second = Observable.create((observer) => {
  return first.subscribe(
    function onNext(value) {
      /* do something with value like: */
      observer.next(value);
    },
    function onError(error) {
      observer.error(error);
    },
    function onComplete() {
      someOtherObservable.take(1).subscribe(
        function onNext(value) {
          observer.next(value);
        },
        function onError(error) {
          observer.error(error);
        },
        function onComplete() {
          observer.complete();
        }
      );
    }
  );
}).subscribe(
  function onNext(value) {
    console.log(value);
  },
  function onError(error) {
    console.error(error);
  },
  function onComplete() {
    console.log("Done!");
  }
);

コンソールを見ると、上記の例は次のように出力されます。

1

6

完了!


これは、ソースからタイムスパンT内で最初のXのみを処理し、D遅延で間隔を空けて結果を出力する独自のカスタム「cluster(T、X、D)」演算子を作成するために必要な画期的なものでした。ありがとうございました!
wonkim00 2018

お役に立ててうれしいです。これに気付いたときも、とても啓発的でした。
c1moore 2018

2

結果を出す前にシグナルを待つTypeScriptで書かれたカスタム演算子は次のとおりです。

export function waitFor<T>(
    signal$: Observable<any>
) {
    return (source$: Observable<T>) =>
        new Observable<T>(observer => {
            // combineLatest emits the first value only when
            // both source and signal emitted at least once
            combineLatest([
                source$,
                signal$.pipe(
                    first(),
                ),
            ])
                .subscribe(([v]) => observer.next(v));
        });
}

次のように使用できます。

two.pipe(waitFor(one))
   .subscribe(value => ...);

1
素敵なパターン!あなたはthree.pipe(のwaitFor(1)、のwaitFor(2)、テイク(1))を実行してもすることができます
デヴィッド・Rinck

1

まあ、私はこれがかなり古いことを知っていますが、あなたが必要とするかもしれないものは次のとおりだと思います:

var one = someObservable.take(1);

var two = someOtherObservable.pipe(
  concatMap((twoRes) => one.pipe(mapTo(twoRes))),
  take(1)
).subscribe((twoRes) => {
   // one is completed and we get two's subscription.
})

0

次のようなmergeMap(または彼のエイリアスflatMap)演算子のおかげで、以前のObservableから出力された結果を使用できます。

 const one = Observable.of('https://api.github.com/users');
 const two = (c) => ajax(c);//ajax from Rxjs/dom library

 one.mergeMap(two).subscribe(c => console.log(c))

1
ここから:learnrxjs.io/learn-rxjs/operators/transformation/mergemap-「内部オブザーバブルの放出とサブスクリプションの順序が重要な場合は、concatMapを試してください!」
gsziszi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.