Búsqueda de sitios web

javascript - ¿Cómo hacer que una secuencia Observable espere a que otra se complete antes de emitir?


Supongamos que tengo un Observable, así:

var one = someObservable.take(1);

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

Luego, tengo un segundo Observable:

var two = someOtherObservable.take(1);

Ahora, quiero subscribe() a two, pero quiero asegurarme de que one haya terminado antes de que se ejecute el suscriptor two.

¿Qué tipo de método de almacenamiento en búfer puedo usar en dos para hacer que el segundo espere a que el primero se complete?

Supongo que estoy buscando pausar dos hasta que uno esté completo.

Respuesta #1

Un par de formas que se me ocurren

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

Respuesta #2

Si quieres asegurarte de que se mantenga el orden de ejecución, puedes usar flatMap como en el siguiente ejemplo

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

El resultado sería:

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

Respuesta #3

skipUntil() con last()

skipUntil : ignorar elementos emitidos hasta que otro observable haya emitido

last: emitir el último valor de una secuencia (es decir, esperar hasta que se complete y luego emitir)

Tenga en cuenta que cualquier cosa emitida desde el observable pasado a skipUntil cancelará el salto, por lo que necesitamos agregar last() - para esperar a que la secuencia se complete.

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

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


Posible problema: Ten en cuenta que last() por sí solo generará un error si no se emite nada. El operador last() sí tiene un parámetro default, pero solo cuando se utiliza junto con un predicado. Creo que si esta situación es un problema para ti (si sequence2$ puede completarse sin emitir), entonces una de estas opciones debería funcionar (actualmente no probado):

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

Tenga en cuenta que undefined es un elemento válido para emitir, pero en realidad podría ser cualquier valor. También tenga en cuenta que esta es la tubería adjunta a sequence2$ y no a la tubería main$.

Respuesta #4

Aquí hay una forma reutilizable de hacerlo (es TypeScript pero puedes adaptarlo a JS):

function waitFor<T>(signal: Observable<any>) {
    return (source: Observable<T>) => signal.pipe(
        first(),
        switchMap(_ => source),
    );
}

y puedes usarlo como cualquier operador:

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

Básicamente, es un operador que retrasa la suscripción al observable de origen hasta que el observable de señal emite el primer evento.

Respuesta #5

Aquí hay otra posibilidad más aprovechando el selector de resultados de 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 */ 
  });

Dado que el selector de resultados de switchMap ha sido depreciado, aquí hay una versión actualizada

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

Artículos relacionados