66 votes

Est-ce une bonne pratique d'utiliser Observable avec async / wait?

J'utilise angulaire 2 http commun qui retourne un Observable, mais je suis confronté à un problème que mon code aime un maillage lorsque j'utilise l'appel Observable imbriqué:

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

       })
   })
})
 

Maintenant, je veux utiliser async / wait pour éviter cela, mais async / wait ne fonctionne qu'avec Promise. Je sais qu'Observable peut être converti en Promise mais comme je le sais, ce n'est pas une bonne pratique. Alors, que dois-je faire ici?

BTW, ce sera bien si quelqu'un peut me donner un exemple de code pour résoudre ce problème avec async / wait: D

81voto

Pac0 Points 7866

Le chaînage des Observables dans l'ordre, que vous voulez faire dans votre code

Concernant votre exemple de code, si vous voulez de la chaîne d'Observables (en déclencher un autre après la précédente émet), utilisez flatMap (ou switchMap) à cet effet :

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

C'est mieux de la pratique par rapport à la nidification, car cela va rendre les choses plus claires et vous aider à éviter de rappel de l'enfer, que les Observables et les Promesses étaient censés aider à prévenir en premier lieu.

Aussi, envisager l'utilisation d' switchMap au lieu de flatMap, fondamentalement, il permettra à "annuler", les autres demandes si le premier émet une nouvelle valeur. Agréable à utiliser si le premier Observables qui déclenche le reste est certains événement de clic sur un bouton, par exemple.

Si vous n'avez pas besoin de vos diverses demandes d'attente, tour à tour, les uns les autres, vous pouvez utiliser forkJoin ou zip pour démarrer tout à la fois, voir @Dan Macak réponse pour plus de détails et d'autres idées.


Angulaire 'async' pipe et Observables de bien travailler ensemble

Concernant les phénomènes Observables et Angulaire, vous pouvez parfaitement utiliser | async pipe dans un Angulaires du modèle plutôt que de souscrire à l'Observable dans votre code de composant, pour obtenir la valeur(s) émis par cet Observable


ES6 async / await et des Promesses plutôt que des Observables ?

si vous ne vous sentez pas à l'aide Observable directement, vous pouvez simplement utiliser .toPromise() sur votre Observables, et puis certains async/await instructions.

Si votre Observable est censé retourner qu'un seul résultat (comme c'est le cas de la base d'appels d'API) , Observable peut être considéré comme tout à fait équivalent à une Promesse.

Cependant, je ne suis pas sûr qu'il y est besoin de le faire, compte tenu de toutes les choses qui Observables déjà fournir (pour les lecteurs : en éclairant les contre-exemples sont les bienvenus!) . Je serais plus en faveur de l'utilisation des Observables à chaque fois que vous pouvez, comme un exercice d'entraînement.


Intéressant article de blog sur ce qui (et il y en a beaucoup d'autres):

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

Le toPromise fonction est en fait un peu délicat, car il n'est pas vraiment un "opérateur", c'est plutôt un RxJS spécifique des moyens de en souscrivant à un fait Observable et l'envelopper dans une promesse. La promesse permettra de résoudre à la dernière émise valeur de l'Observable, une fois la Observables complète. Cela signifie que si l'Observable émet l' la valeur "bonjour", puis attend 10 secondes avant la fin, le retour de l' promesse d'attendre 10 secondes avant de résoudre "salut". Si l'Observable ne se termine jamais, alors, la Promesse n'a jamais résolu.

REMARQUE: l'utilisation de toPromise() est un antipattern sauf dans le cas où vous êtes traiter avec une API qui s'attend à une Promesse, comme async-attendent

(l'emphase est mienne)


L'exemple que vous avez demandé

BTW, ça serait sympa si quelqu'un peut me donner un exemple de code pour résoudre ce avec async/await :D

Exemple, si vous avez vraiment envie de le faire (probablement avec quelques erreurs, ne peut pas vérifier, dès maintenant, n'hésitez pas à corriger)

// 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
}

Dans le cas où vous pouvez commencer à toutes les demandes simultanément, await Promise.all() qui devrait être plus efficace, car aucun des appels dépend de la suite les uns des autres. (comme le ferait forkJoin faire avec 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 promises results,
    // res[0], res[1], res[2] respectively.
}

26voto

Dan Skočdopole Points 924

Comme @Pac0 déjà élaboré sur les différentes solutions bien, je vais juste ajouter un angle légèrement différent.

Le mélange des Promesses et des Observables

Personnellement, je préfère ne pas mélanger les Promesses et les Observables - qui est ce que vous obtenez en utilisant async attendent avec Observables, parce que même si elles se ressemblent, elles sont très différentes.

  • Les promesses sont toujours async, Observables pas nécessairement
  • Les promesses de représenter la juste valeur 1, Observables 0, 1 ou plusieurs
  • Des promesses ont une utilisation très limitée, vous ne pouvez pas par exemple. annuler (mettre de côté ES à côté des propositions), les phénomènes Observables sont donc beaucoup plus puissant dans leur utilisation (vous pouvez gérer par exemple plusieurs WS liens avec eux, essayer avec des Promesses)
  • Leur Api diffèrent grandement

L'utilisation de Promesses Angulaire

Maintenant, même s'il est parfois valable pour une utilisation à la fois, en particulier avec Angulaires je pense que l'on devrait envisager d'aller aussi loin avec RxJS que possible. Les raisons d'être:

  • Une grande partie de l'Angulaire de l'API utilise des Observables (routeur, http ...), donc une sorte de va avec et non pas contre-courant (sans jeu de mot) à l'aide de RxJS, sinon on aurait à convertir les Promesses de tous les temps tout en faisant de la place pour la perte de possibilités RxJS fournit
  • Angulaire a de puissants async tuyau qui permet de composer votre ensemble de l'application de flux de données de flux qui permet de filtrer, d'associer et de faire toutes les modifications que vous voulez sur elle sans interrompre le flux de données en provenance du serveur sans un seul besoin pour thening ou en vous abonnant. De cette façon, vous n'avez pas besoin de déballer les données ou l'affecter à certaines variables auxiliaires, les données des flux de services par le biais de phénomènes Observables directement le modèle, qui est tout simplement magnifique.

Il y a certains cas où la Promesse peut encore briller. Par exemple, ce qu'il me manque dans rxjs Tapuscrit de types de concept de l' unique. Si vous êtes à la création d'une API pour être utilisé par d'autres, le retour Observable n'est pas tout que de dire: vous recevrez 1 de la valeur, beaucoup, ou est-ce juste complet? Vous devez écrire un commentaire pour expliquer cela. D'autre part, la Promesse a beaucoup plus claire contrat dans ce cas. Il sera toujours résoudre avec une valeur de 1 ou de rejeter avec erreur (sauf si il se bloque jamais, bien sûr).

Généralement, vous avez certainement n'avez pas besoin d'avoir seulement des Promesses ou seulement Observables dans votre projet. Si vous voulez juste pour exprimer avec une valeur que quelque chose a été achevé (suppression d'un utilisateur, la mise à jour de l'utilisateur), et vous souhaitez réagir sur elle sans l'intégrer à un ruisseau, la Promesse est le moyen plus naturel de le faire. Aussi, à l'aide de async/await vous donne le pouvoir écrire du code dans l'ordre et, par conséquent, en simplifiant considérablement, de sorte que si vous avez besoin d'une gestion avancée de valeurs entrantes, vous pouvez rester avec Promesse.


De retour à votre exemple

Donc, ma recommandation est d' embrasser à la fois la puissance de RxJS et Angular. Pour revenir à votre exemple, vous pouvez écrire le code suivant (crédits pour l'idée de @Vayrex):

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

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

Ce bout de code va tirer 3 demandes et une fois toutes ces demande Observables avez terminé, abonnement de rappel à l' forkJoin , vous obtiendrez les résultats dans un tableau, et comme l'a dit, vous pouvez vous abonner manuellement (comme dans l'exemple) ou de le faire de manière déclarative à l'aide de result$ et async pipe dans le modèle.

À l'aide de Observable.zip vous obtiendrez le même résultat ici, la différence entre forkJoin et zip , c'est que l'ancien émet des dernières valeurs de l'intérieure Observables, ce dernier combine les premières valeurs de l'intérieur Observables, puis la deuxième valeurs, etc.


Edit: Depuis que vous avez besoin des résultats des requêtes HTTP, utilisez flatMap approche @Pac0 de réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X