Saltar al contenido

¿Es una buena práctica usar Observable con async / await?

Solución:

Encadenando Observables en secuencia, como quiera hacer en su código

Con respecto a su ejemplo de código, si desea encadenar Observables (activar otro después de las emisiones anteriores), use flatMap (o switchMap) para este propósito :

this.serviceA.get()
  .flatMap((res1: any) => this.serviceB.get())
  .flatMap((res2: any) => this.serviceC.get())
  .subscribe( (res3: any) => { 
    .... 
  });

Esta es una mejor práctica en comparación con el anidamiento, ya que esto aclarará las cosas y lo ayudará a evitar el infierno de devolución de llamada, que se suponía que Observable y Promises ayudarían a prevenir en primer lugar.

Además, considere usar switchMap en lugar de flatMap, básicamente permitirá ‘cancelar’ las demás solicitudes si la primera emite un nuevo valor. Es bueno usarlo si el primer Observable que activa el resto es un evento de clic en un botón, por ejemplo.

Si no necesita que sus diversas solicitudes se esperen una a la otra, puede usar forkJoin o zip para iniciarlos todos a la vez, consulte las respuestas de @Dan Macak para obtener detalles y otras ideas.


La tubería angular ‘asíncrona’ y los Observables funcionan bien juntos

En cuanto a Observables y Angular, puedes usar perfectamente | async pipe en una plantilla angular en lugar de suscribirse al Observable en el código de su componente, para obtener los valores emitidos por este Observable


ES6 async / await y Promises en lugar de Observables?

Si no se siente usando Observable directamente, simplemente puede usar .toPromise() en su Observable, y luego algunas instrucciones asincrónicas / en espera.

Si se supone que su Observable devuelve solo un resultado (como es el caso de las llamadas API básicas), un Observable puede verse como bastante equivalente a una Promesa.

Sin embargo, no estoy seguro de que sea necesario hacer eso, considerando todas las cosas que Observable ya proporciona (a los lectores: ¡son bienvenidos contraejemplos esclarecedores!). Yo estaría más a favor de usar Observables siempre que puedas, como ejercicio de entrenamiento.


Algún artículo de blog interesante sobre eso (y hay muchos otros):

https://medium.com/@benlesh/rxjs-observable-interop-with-promises-and-async-await-bebb05306875

La función toPromise es en realidad un poco complicada, ya que en realidad no es un “operador”, sino que es un medio específico de RxJS para suscribirse a un Observable y envolverlo en una promesa. La promesa se resolverá al último valor emitido del Observable una vez que el Observable se complete. Eso significa que si el Observable emite el valor “hola” y luego espera 10 segundos antes de completarse, la promesa devuelta esperará 10 segundos antes de resolver “hola”. Si lo Observable nunca se completa, entonces la Promesa nunca se resuelve.

NOTA: usar toPromise () es un antipatrón, excepto en los casos en los que se trata de una API que espera una Promesa, como async-await

(énfasis mío)


El ejemplo que solicitaste

Por cierto, sería bueno si alguien me pudiera dar un código de ejemplo para resolver esto con async / await: D

Ejemplo si tu De Verdad quiero hacerlo (probablemente con algunos errores, no puedo verificar en este momento, no dude en corregirlo)

// Warning, probable anti-pattern below
async myFunction() {
    const res1 = await this.serviceA.get().toPromise();
    const res2 = await this.serviceB.get().toPromise();
    const res3 = await this.serviceC.get().toPromise();
    // other stuff with results
}

En el caso de que pueda iniciar todas las solicitudes simultáneamente, await Promise.all() que debería ser más eficiente, porque ninguna de las llamadas depende del resultado de cada una. (como lo haría forkJoin hacer con Observables)

async myFunction() {
    const promise1 = this.serviceA.get().toPromise();
    const promise2 = this.serviceB.get().toPromise();
    const promise3 = this.serviceC.get().toPromise();

    let res = await Promise.all([promise1, promise2, promise3]);

    // here you can retrieve promises results,
    // in res[0], res[1], res[2] respectively.
}

Como @ Pac0 ya elaboró ​​bien las diversas soluciones, solo agregaré un ángulo ligeramente diferente.

Mezcla de promesas y observables

Yo personalmente prefiero no mezclar promesas y observables – que es lo que obtienes al usar async await con Observables, porque aunque se ven similares, son muy diferentes.

  • Las promesas son siempre asincrónicas, los observables no necesariamente
  • Las promesas representan solo 1 valor, Observables 0, 1 o muchos
  • Las promesas tienen un uso muy limitado, no se puede, por ejemplo. cancelarlos (dejar de lado las próximas propuestas de ES), los Observables son mucho más poderosos en su uso (puede administrar, por ejemplo, múltiples conexiones de WS con ellos, intente eso con Promises)
  • Sus API difieren enormemente

Uso de promesas en Angular

Ahora bien, aunque a veces es válido usar ambos, especialmente con Angular, creo que uno debería considerar ir lo más lejos posible con RxJS. Las razones son:

  • Gran parte de la API angular usa Observables (enrutador, http …), por lo que un tipo de va con y no en contra de la secuencia (sin juego de palabras) mediante el uso de RxJS, de lo contrario, uno tendría que convertir a Promesas todo el tiempo mientras compensa las posibilidades perdidas que proporciona RxJS
  • Angular tiene poder async tubería que permite componer todo el flujo de datos de la aplicación de flujos que usted filtra, combina y realiza cualquier modificación que desee sin interrumpir el flujo de datos que proviene del servidor sin una sola necesidad de suscripción o suscripción. De esta manera, no necesita desenvolver los datos o asignarlos a algunas variables auxiliares, los datos simplemente fluyen desde los servicios a través de Observables directamente a la plantilla, lo cual es simplemente hermoso.

Sin embargo, hay algunos casos en los que La promesa aún puede brillar. Por ejemplo, lo que me falta en rxjs Los tipos de TypeScript son un concepto de soltero. Si está creando una API para que la utilicen otros, devolver Observable no es tan revelador: ¿Recibirá 1 valor, muchos o simplemente se completará? Tienes que escribir un comentario para explicarlo. Por otro lado, Promise tiene un contrato mucho más claro en este caso. Siempre se resolverá con 1 valor o se rechazará con error (a menos que se cuelgue para siempre, por supuesto).

En general, definitivamente no es necesario tener solo promesas o solo observables en su proyecto. Si solo quieres expresar con un valor ese algo Se completó (eliminar usuario, actualizar usuario), y desea reaccionar sin integrarlo a algún flujo, Promise es la forma más natural de hacerlo. Además, usando async/await le da el poder de escribir código de manera secuencial y, por lo tanto, lo simplifica enormemente, por lo que, a menos que necesite una administración avanzada de los valores entrantes, puede quedarse con Promise.


De vuelta a tu ejemplo

Entonces mi recomendación es abrazar tanto el poder de RxJS como de Angular. Volviendo a su ejemplo, puede escribir el código de la siguiente manera (créditos por la idea a @Vayrex):

this.result$ = Observable.forkJoin(
  this.serviceA.get(),
  this.serviceB.get(),
  this.serviceC.get()
);

this.result$.subscribe(([resA, resB, resC]) => ...)

Este fragmento de código disparará 3 solicitudes y una vez que todas esas solicitudes Observables se hayan completado, se devolverá la llamada de suscripción a forkJoin obtendrá los resultados en una matriz, y como se dijo, puede suscribirse manualmente (como en el ejemplo) o hacer esto declarativamente usando result$ y async tubería en la plantilla.

Utilizando Observable.zip obtendría el mismo resultado aquí, la diferencia entre forkJoin y zip es que el primero emite solo los últimos valores de los Observables internos, el último combina los primeros valores de los Observables internos, luego los segundos valores, etc.


Editar: Dado que necesita los resultados de solicitudes HTTP anteriores, utilice flatMap enfoque en la respuesta de @ Pac0.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *