¿Cómo puedo "esperar" en un Rx Observable?

106

Me gustaría poder esperar en un observable, p. Ej.

const source = Rx.Observable.create(/* ... */)
//...
await source;

Un intento ingenuo da como resultado que la espera se resuelva de inmediato y no bloquee la ejecución.

Editar: El pseudocódigo para mi caso de uso previsto completo es:

if (condition) {
  await observable;
}
// a bunch of other code

Entiendo que puedo mover el otro código a otra función separada y pasarlo a la devolución de llamada de suscripción, pero espero poder evitarlo.

CheapSteaks
fuente
¿No puede mover el código restante (que desea esperar por la fuente) a una .subscribe()llamada de método?
StriplingWarrior

Respuestas:

132

Tienes que pasarle una promesa await. Convierta el próximo evento del observable en una promesa y espere eso.

if (condition) {
  await observable.first().toPromise();
}

Nota de edición: esta respuesta originalmente usaba .take (1) pero se cambió para usar .first (), lo que evita que el problema de la Promesa nunca se resuelva si la transmisión termina antes de que llegue un valor.

Macil
fuente
3
En lugar de tomar (1), ¿podría usar await observable.first().toPromise();?
apricity
14
@apricity Si no hubo valores al finalizar, first()resultará en rechazo y take(1)resultará en promesa pendiente.
Estus Flask
6
@apricity @AgentME En realidad, NO debe usar take(1)ni first()en casos como este. Como está esperando que ocurra exactamente UN evento, debe usar el single()cual lanzará una excepción si hay más de 1, mientras que no lanzará una excepción cuando no hay ninguna. Si hay más de uno, es probable que haya algún error en su código / modelo de datos, etc. Si no usa single, terminará eligiendo arbitrariamente el primer artículo que regresa sin advertir que hay más. Debería tener cuidado en su predicado sobre la fuente de datos ascendente para mantener siempre el mismo orden.
ntziolis
3
No olvide la importación:import 'rxjs/add/operator/first';
Stephanie
7
Ahora que toPromise () está en desuso, ¿cómo deberíamos hacer esto?
Jus10
26

Probablemente tenga que ser

await observable.first().toPromise();

Como se señaló anteriormente en los comentarios, existe una diferencia sustancial entre los operadores take(1)y first()cuando hay un observable completo vacío.

Observable.empty().first().toPromise()dará como resultado un rechazo EmptyErrorque se puede manejar en consecuencia, porque realmente no había ningún valor.

Y Observable.empty().take(1).toPromise()resultará en una resolución con undefinedvalor.

Matraz Estus
fuente
Realmente notake(1) rendirá una promesa pendiente. Rendirá una promesa resuelta con . undefined
Johan t Hart
Gracias por darte cuenta, eso es correcto. No estoy seguro de por qué la publicación difirió, posiblemente el comportamiento cambió en algún momento.
Estus Flask
8

Necesitará awaituna promesa, por lo que querrá usar toPromise(). Consulte esto para obtener más detalles sobre toPromise().

Josh Durham
fuente
4

Si toPromiseestá desaprobado para usted, puede usarlo, .pipe(take(1)).toPromisepero como puede ver aquí , no está desaprobado.

Así que por favor use toPromise(RxJs 6) como se dijo:

//return basic observable
const sample = val => Rx.Observable.of(val).delay(5000);
//convert basic observable to promise
const example = sample('First Example')
  .toPromise()
  //output: 'First Example'
  .then(result => {
    console.log('From Promise:', result);
  });

ejemplo async / await:

//return basic observable
const sample = val => Rx.Observable.of(val).delay(5000);
//convert basic observable to promise
const example = await sample('First Example').toPromise()
// output: 'First Example'
console.log('From Promise:', result);

Leer más aquí .

Y elimine esta afirmación incorrecta que dice que toPromiseestá obsoleta.

Emerica
fuente