Bir Gözlenebilir sekansı yaymadan önce diğerinin tamamlanmasını nasıl bekletebilirim?


93

Diyelim ki bende olduğu Observablegibi:

var one = someObservable.take(1);

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

O zaman bir saniyem var Observable:

var two = someOtherObservable.take(1);

Şimdi, ben istiyorum subscribe()için two, ama emin olmak için, oneönce tamamladı twoabone ateşlenir.

twoİkincisinin ilkinin tamamlanmasını beklemesi için ne tür bir arabellekleme yöntemi kullanabilirim ?

Sanırım tamamlanana twokadar duraklatmak istiyorum one.


1
Bunun cevabının .exhaustMap () yöntemi olduğuna inanıyorum, ancak onu nasıl uygulayacağımı biliyormuş gibi yapmazdım - tam açıklama burada: blog.angular-university.io/rxjs-higher-order-mapping
Peter Nixey

Yanıtlar:


56

Düşünebileceğim birkaç yol

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
Sonunda pauseve resumeyerine publishve kullandım connect, ancak örnek iki esas olarak izlediğim yol.
Stephen

1
Bu yöntem her zaman ilk observable ( one) çözümünü two, subscribe () - işlevinin içindeki ikinci ( ) öğesinden önce mi yapacak ?
John

Neden kullanmıyorsun Observable.forkJoin()? Bu bağlantıya bakın learnrxjs.io/operators/combination/forkjoin.html
mspasiuk

18
@mspasiuk OP'lerin gereksinimine göre, yalnızca ikincisinin ilk tamamlandıktan sonra abone olmasını istediler . forkJoinaynı anda abone olur.
paulpdaniels

18

Yürütme sırasının korunduğundan emin olmak istiyorsanız, aşağıdaki örnek olarak flatMap'i kullanabilirsiniz.

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

Sonuç şöyle olacaktır:

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

16

skipUntil () ile last ()

skipUntil: başka bir gözlemlenebilir görüntü çıkana kadar yayılan öğeleri yoksay

last: bir diziden son değeri yayınlar (yani tamamlanana kadar bekleyin ve sonra yayınlayın)

Gözlemlenebilirden iletilen herhangi bir şeyin skipUntilatlamayı iptal edeceğini unutmayın, bu yüzden last()akışın tamamlanmasını beklemek için eklememiz gerekir .

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

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


Olası sorun: Hiçbir şey yayınlanmadığında last()kendi kendine hata yapacağını unutmayın . last()Operatör var defaultbir yüklemi ile bağlantılı olarak kullanılan parametre ama sadece. Bence bu durum sizin için bir sorunsa (eğer sequence2$yayılmadan tamamlanabilirse) o zaman bunlardan biri çalışmalıdır (şu anda test edilmemiştir):

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

Bunun undefinedyayınlanacak geçerli bir öğe olduğunu, ancak aslında herhangi bir değer olabileceğini unutmayın. Ayrıca bunun boruya sequence2$değil boruya bağlı olduğunu unutmayın main$.


Çok beceriksiz demo: angular-vgznak.stackblitz.io Konsol tepsisini açmak için tıklamanız gerekiyor
Simon_Weaver

Söz diziminiz yanlış. skipUntil doğrudan bir gözlemlenebilir duruma eklenemez, aksi takdirde şu hatayı alırsınız: "Özellik" skipUntil "," Gözlemlenebilir <any> "türünde mevcut değil." Önce .pipe ()
London804

Evet, bu boru gerekmeden önceki eski bir cevaptır. Bahsettiğin için teşekkürler. Şimdi güncellerdim ama telefonumdayım. Cevabı düzenlemekten çekinmeyin.
Simon_Weaver

13

SwitchMap'in sonuç seçicisinden faydalanan başka bir olasılık daha var

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'in sonuç seçicisi amortismana tabi tutulduğundan, işte güncellenmiş bir sürüm

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

8

İşte bunu yapmanın yeniden kullanılabilir bir yolu (typcript ancak js'ye uyarlayabilirsiniz):

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

ve herhangi bir operatör gibi kullanabilirsiniz:

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

Temelde, gözlemlenebilir sinyal ilk olayı yayana kadar gözlemlenebilir kaynak üzerindeki aboneliği erteleyen bir operatördür.


bu yeniden kullanılabilir işlevin rxswift sürümü var mı
sujith1406

6

İkinci gözlemlenebilir ise sıcak , orada başka bir yolu yapmak için duraklama / özgeçmiş :

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

Ayrıca, duraklatma açıkken verileri tutmak için arabelleğe alınmış pausableBuffered sürümünü de kullanabilirsiniz .


2

İşte bir tane daha, ama daha açık ve sezgisel hissediyorum (veya en azından Sözlere alışkınsanız doğal), yaklaşın. Temel olarak, Observable.create()sarmak için kullanarak oneve twotek bir Gözlemlenebilir olarak bir Gözlemlenebilir oluşturursunuz. Bu, nasıl Promise.all()çalışabileceğine çok benzer .

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

Peki burada neler oluyor? İlk önce yeni bir Observable oluşturuyoruz. Uygun bir Observable.create()şekilde adlandırılmış olan işlev onSubscription, gözlemciye aktarılır (geçtiğiniz parametrelerden oluşturulur subscribe()), bu da resolvevereject yeni bir Promise oluştururken tek bir nesneye tek bir nesnede birleştirilen . Sihri bu şekilde çalıştırıyoruz.

' onSubscriptionDa ilk Gözlemlenebilir'e abone oluruz (yukarıdaki örnekte buna denir one). Nasıl başa çıkacağımız nextve errorsize kalmış, ancak örneğimde sağlanan varsayılan genel olarak uygun olmalıdır. Ancak, completeetkinliği aldığımızdaone artık bitti, bir sonraki Gözlemlenebilir'e abone olabiliriz; böylelikle birinci Gözlemlenebilir olanı birincisi tamamlandıktan sonra ateşler.

İkinci Gözlemlenebilir için sağlanan örnek gözlemci oldukça basittir. Temel olarak, secondartık twoOP'de olmasını beklediğiniz gibi davranıyor. Daha spesifik olarak, secondtarafından yayılan ilk ve yalnızca ilk değeri yayar someOtherObservable(çünkütake(1) hata olmadığını varsayarak ) ve sonra tamamlayacaktır.

Misal

İşte örneğimin gerçek hayatta çalıştığını görmek istiyorsanız kopyalayıp yapıştırabileceğiniz eksiksiz, çalışan bir örnek:

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

Konsolu izlerseniz, yukarıdaki örnek yazdırılacaktır:

1

6

Bitti!


Bu, kaynaktan T zaman aralığı içinde yalnızca ilk X yayımını işleyen ve sonuçları D gecikmesiyle aralıklı olarak yayan kendi özel 'küme (T, X, D)' operatörümü oluşturmak için ihtiyacım olan buluştu. Teşekkür ederim!
wonkim00

Yardımcı olmasına sevindim, bunu fark ettiğimde de çok aydınlatıcı oldu.
c1moore

2

Sonuçları yayınlamadan önce bir sinyal bekleyen TypeScript ile yazılmış özel bir operatör:

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

Bunu şu şekilde kullanabilirsiniz:

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

1
güzel desen! Üç. Çekme bile yapabilirsiniz (waitFor (one), waitFor (two), take (1))
David Rinck

1

Pekala, bunun oldukça eski olduğunu biliyorum ama ihtiyacın olabileceğine inanıyorum:

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 (veya diğer adı flatMap ) operatörü sayesinde önceki Observable'dan çıkan sonucu şu şekilde kullanabilirsiniz :

 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
buradan: learnrxjs.io/learn-rxjs/operators/transformation/mergemap - "İç gözlemlenebilirlerin emisyon sırası ve aboneliği önemliyse, concatMap'i deneyin!"
gsziszi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.