web-dev-qa-db-ja.com

あるRxJS Observableシーケンスを、別のシーケンスが完了するのを待ってから送信するにはどうすればよいですか?

次のように、Observableがあるとします。

var one = someObservable.take(1);

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

次に、2番目の観測対象があります。

var two = someOtherObservable.take(1);

ここで、twoにサブスクライブしたいのですが、oneサブスクライバーが起動される前にtwoが完了したことを確認したいです。 twoでどのようなバッファリング方法を使用して、2番目のメソッドが最初のメソッドが完了するのを待つことができますか?

twoが完了するまで、oneを一時停止しようとしていると思います。

47
Stephen

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

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));
30
paulpdaniels

skipUntil()とlast()

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

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

skipUntilに渡されたobservableから放出されたものはすべてスキップをキャンセルするため、ストリームが完了するのを待つために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$に接続されたパイプであることに注意してください。

12
Simon_Weaver

実行順序が保持されるようにする場合は、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"
8
Nikos Tsokos

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 */ 
});
7
Joe King

2番目のオブザーバブルがhotの場合、 別の方法 to(pause/resume)

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を使用して、一時停止がオンの間にデータを保持できます。

5
Anton

再利用可能な方法を次に示します(TypeScriptですが、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));

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

3
Andrei Tătar

RxJSに簡単に翻訳できるRxSwiftのかなり直感的でシンプルな実装を次に示します。

Observable.never().takeUntil(signalingObservable).concat(delayedObservable)

1
kball

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))
0
Tktorza

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

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
itay oded