web-dev-qa-db-fra.com

Angular 6 / Rxjs - Les bases: succès des observables, erreur, enfin

Je construis une architecture sur la dernière Angular 6 et, venant d'AngularJS, il y a quelque chose qui ne me convient pas: le traitement de base d'une requête HTTP.

Donc, pour le bien de la question, disons que je veux un observable. Parce que cela semble être l'avenir de Angular.

Je suis passé de quelque chose de très élégant, dans AngularJS:

   service.getAll()
    .then(onSuccess) // I process the data
    .catch(onError) // I do whatever needed to notify anyone about the issue
    .finally(onFinally); // I stop the loading spinner and other stuff

Maintenant, dans Angular 6/RxJS 6, je ne comprends pas pourquoi tout est si compliqué et ne semble pas correct.

Je pourrais trouver deux façons de faire la même chose que ci-dessus:

  1. Le tuyau complet

    this.service.getAll()
        .pipe(
            map((data) => this.onSuccess(data)),
            catchError(error => of(this.handleError(error))),
            finalize(() => this.stopLoading())
        )
        .subscribe();
    

Etant donné que nous devons utiliser un tuyau pour la finalisation, je pourrais aussi bien utiliser un tuyau pour tout, je pense qu'il est préférable de tout avoir dans le même ordre. Mais maintenant, nous devons lancer quelque chose, appelé "de" (pas très facile à comprendre) et je n'aime pas ça.

  1. Le half-pipe J'essaie donc une autre idée, avec seulement le tuyau dont j'ai besoin (finaliser) et je garde les rappels d'abonnement.

    this.service.getAll()
    .pipe(
        finalize(() => this.stopLoading())
    )
    .subscribe(
        (data) => this.onSuccess(data),
        (error) => this.handleError(error)
    );
    

Enfin bon. N'est-ce pas un peu en arrière? Nous avons toujours des rappels sans noms réels, et nous finalisons avant de lire le traitement et l'erreur. Bizarre.

Donc, il y a quelque chose que je ne comprends vraiment pas. Et je ne trouve rien en ligne concernant cette question fondamentale. Vous avez soit quelqu'un qui veut "le succès et finalement", ou "le succès et l'erreur" mais personne ne veut les 3 d'entre eux. Peut-être que je suis trop vieux et que je ne comprends pas la nouvelle meilleure pratique à ce sujet (dans l’affirmative, éduquez-moi!).

Mon besoin est simple:
1. Je veux traiter les données que je reçois d'un service
2. Je veux obtenir l'erreur afin d'afficher à l'utilisateur
3. Je veux arrêter le chargement de la roulette que je viens de démarrer avant l'appel ou passer un autre appel une fois que le premier succès est complet ou qu'une erreur est survenue (je veux vraiment un enfin)

Comment gérez-vous votre appel HTTP de base avec observable?

(Je ne veux pas de .toPromise, s'il-vous-plaît, je veux comprendre comment faire avec les nouveaux éléments)

20
Simon Peyou

Je pense qu'il y a un malentendu clé:

Vous avez soit quelqu'un qui veut "le succès et finalement", ou "le succès et l'erreur" mais aucun ne veut les 3 d'entre eux.

Ce n'est pas tout à fait vrai. Chaque observable peut envoyer zéro ou plusieurs notifications next et une notification error ou complete mais jamais les deux. Par exemple, lorsque vous passez un appel HTTP réussi, vous recevez une notification next et un complete. En cas d'erreur de requête HTTP, vous ne recevez qu'une seule notification error et c'est tout. Voir http://reactivex.io/documentation/contract.html

Cela signifie qu'un observable n'émettra à la fois error et complete.

Et puis il y a l'opérateur finalize. Cet opérateur est appelé lors de la mise au rebut de la chaîne (ce qui inclut également la désinscription en clair). En d'autres termes, il est appelé après les deux notifications error et complete.

Donc, le deuxième exemple que vous avez est correct. Je comprends qu'il semble étrange que vous incluiez finalize avant de vous abonner, mais en fait, chaque émission de la source Observable va d'abord de haut en bas là où elle atteint les abonnés et là si sa error ou complete notification il déclenche les gestionnaires d'élimination de bas en haut (dans l'ordre inverse) et à ce stade, finalize est appelé. Voir https://github.com/ReactiveX/rxjs/blob/master/src/internal/Subscriber.ts#L150-L152

Dans votre exemple, utiliser finalize revient à ajouter vous-même le gestionnaire de suppression dans un objet Subscription.

const subscription = this.service.getAll()
  .subscribe(
    (data) => this.onSuccess(data),
    (error) => this.handleError(error)
  );

subscription.add(() => this.stopLoading());
24
martin

La méthode subscribe of Observable accepte 3 fonctions optionnelles en tant que paramètres

  • le premier à traiter les données qui accompagnent l'événement soulevé par l'Observable
  • le second pour traiter toute erreur éventuelle
  • le troisième à faire quelque chose à la fin de l'Observable

Donc, si je comprends bien, ce que vous voulez peut être réalisé avec un code qui ressemble à ceci

this.service.getAll()
.subscribe(
    data => this.onSuccess(data),
    error => this.handleError(error),
    () => this.onComplete()
);

Considérez que l'utilisation d'Observables pour les appels http peut présenter des avantages lorsque vous souhaitez réessayer (voir l'opérateur retry) si vous avez des conditions de concurrence critique (via l'utilisation de l'opérateur switchMap.). Je pense que ce sont les principales raisons pour lesquelles l’équipe Angular a choisi cette approche pour le client http.

De manière générale, je pense que cela vaut la peine de commencer à savoir comment fonctionnent les Observables et certains des opérateurs les plus importants (en plus de ceux mentionnés ci-dessus, je pense d’abord à mergeMap, filter, reduce - mais alors il y en a beaucoup d'autres) est important car ils peuvent considérablement simplifier de nombreuses tâches dans des environnements asynchrones non bloquants, tels que le navigateur (ou Node, par exemple).

5
Picci

Je pense que la bonne façon consiste à utiliser les fonctions observables: ensuite, err, complète.
Voici un exemple de la manière dont vous pouvez déclencher la fonction complète.
Disons que nous avons un objet BehaviorSubject:

let arr = new BehaviorSubject<any>([1,2]);

Supposons maintenant que nous souhaitons nous y abonner et que nous obtenions la valeur "finish".

let arrSubscription = arr.asObservable().subscribe(
  data => {
      console.log(data)
      if(data === 'finish') {
        arr.complete()
      }
  },
  err => {
      console.log(err)
  },
  () => {
      console.log("Complete function triggered.")
  }
);
arr.next([3,4])
arr.next('finish')
arr.next([5,6])

Le journal de la console est:

[1,2]
[3,4]
finish
Complete function triggered.

Depuis que nous avons déclenché la fonction complète, le dernier .next à notre BehaviorSubject ne sera pas déclenché, car err et complete function sont des terminateurs de l'abonnement.
Ceci est juste un exemple de la façon dont vous pouvez déclencher la fonction complète, à partir de là, vous pouvez faire ce que vous voulez.

1
dAxx_