web-dev-qa-db-fra.com

Est-il nécessaire de se désabonner des observables créées par les méthodes Http?

Devez-vous vous désabonner de Angular 2 appels http pour éviter les fuites de mémoire?

 fetchFilm(index) {
        var sub = this._http.get(`http://example.com`)
            .map(result => result.json())
            .map(json => {
                dispatch(this.receiveFilm(json));
            })
            .subscribe(e=>sub.unsubscribe());
            ...
159
born2net

Donc, la réponse est non, pas vous. Ng2 le nettoie tout seul.

La source de service Http, à partir de la source principale Http XHR de Angular:

enter image description here

Remarquez comment il exécute la complete() après avoir obtenu le résultat. Cela signifie qu’il se désabonne à la fin. Donc, vous n'avez pas besoin de le faire vous-même.

Voici un test à valider:

  fetchFilms() {
    return (dispatch) => {
        dispatch(this.requestFilms());

        let observer = this._http.get(`${BASE_URL}`)
            .map(result => result.json())
            .map(json => {
                dispatch(this.receiveFilms(json.results));
                dispatch(this.receiveNumberOfFilms(json.count));
                console.log("2 isUnsubscribed",observer.isUnsubscribed);
                window.setTimeout(() => {
                  console.log("3 isUnsubscribed",observer.isUnsubscribed);
                },10);
            })
            .subscribe();
        console.log("1 isUnsubscribed",observer.isUnsubscribed);
    };
}

Comme prévu, vous pouvez constater qu'il est toujours automatiquement désabonné après avoir obtenu le résultat et terminé avec les opérateurs observables. Ceci se produit sur un timeout (# 3) afin que nous puissions vérifier le statut de l'observable quand tout est terminé et terminé.

Et le résultat

enter image description here

Donc, aucune fuite n’existerait sous forme de Ng2 auto-désinscription!

Il est intéressant de mentionner que cette Observable est classée dans la catégorie finite, contrairement à la infiniteObservablequi est un flux infini de données pouvant être émises comme DOM click auditeur par exemple.

MERCI, @rubyboy de l'aide à ce sujet.

215
born2net

De quoi parlez-vous!!!

OK, donc il y a deux raisons de se désabonner de tout observable. Personne ne semble parler beaucoup de la très importante seconde raison!

1) Nettoyer les ressources. Comme d’autres l’ont dit, c’est un problème négligeable pour les observables HTTP. Ça va juste se nettoyer.

2) Empêchez l'exécution du gestionnaire subscribe.

(Pour HTTP, cela annulera également la demande dans le navigateur - vous ne perdrez donc pas de temps à lire la réponse. Mais c'est en fait un aspect de mon point principal ci-dessous.)

La pertinence du numéro 2 dépendra de ce que fait votre gestionnaire d’abonnement:

Si votre fonction de gestionnaire subscribe() a des effets secondaires indésirables, qu'elle soit fermée ou supprimée, vous devez vous désabonner (ou ajouter une logique conditionnelle) pour l'empêcher de s'exécuter.

Considérons quelques cas:

1) Un formulaire de connexion. Vous entrez votre nom d'utilisateur et votre mot de passe et cliquez sur 'Connexion'. Que se passe-t-il si le serveur est lent et que vous décidez d'appuyer sur Echap pour fermer la boîte de dialogue? Vous allez probablement supposer que vous n'êtes pas connecté, mais si la demande http est renvoyée après avoir appuyé sur échap, vous pourrez toujours exécuter la logique que vous avez là-bas. Cela peut entraîner une redirection vers une page de compte, la définition d'un cookie de connexion indésirable ou d'une variable de jeton. Ce n'est probablement pas ce à quoi votre utilisateur s'attendait.

2) Un formulaire "envoyer un email".

Si le gestionnaire subscribe de 'sendEmail' fait quelque chose comme déclencher une animation 'Votre email est envoyé', vous transfère sur une autre page ou tente d'accéder à tout ce qui a été supprimé, vous risquez d'obtenir des exceptions ou des comportements indésirables.

Veillez également à ne pas supposer que unsubscribe() signifie "annuler". Une fois que le message HTTP est en vol, unsubscribe() n'annulera PAS la demande HTTP si elle est déjà parvenue sur votre serveur. Cela n'annulera que la réponse qui vous reviendra. Et l'e-mail sera probablement envoyé.

Si vous créez l'abonnement pour envoyer l'e-mail directement dans un composant d'interface utilisateur, vous voudrez probablement vous désabonner pour vous en débarrasser, mais si l'e-mail est envoyé par un service centralisé sans interface utilisateur, vous n'en aurez probablement pas besoin.

3) Un composant Angular détruit/fermé. Tous les observables http en cours d'exécution à ce moment-là se termineront et exécuteront leur logique, sauf si vous vous désabonnez dans onDestroy(). Que les conséquences soient triviales ou non dépendra de ce que vous faites dans le gestionnaire d'abonnement. Si vous essayez de mettre à jour quelque chose qui n'existe plus, vous risquez d'obtenir une erreur.

Parfois, vous pouvez avoir certaines actions que vous souhaiteriez si le composant est éliminé, et d'autres non. Par exemple, vous avez peut-être un son "swoosh" pour un courrier électronique envoyé. Vous voudrez probablement que ceci soit lu même si le composant est fermé, mais si vous essayez de lancer une animation sur le composant, cela échouera. Dans ce cas, une solution logique conditionnelle supplémentaire dans subscribe serait la solution - et vous ne voudriez PAS vous désabonner de http observable.

Donc, pour répondre à la question, non, vous n’avez pas besoin de le faire pour éviter les fuites de mémoire. Mais vous devez le faire (souvent) pour éviter que des effets secondaires indésirables ne soient déclenchés par l'exécution d'un code susceptible de générer des exceptions ou de corrompre l'état de votre application.

Conseil: La Subscription contient une propriété closed boolean qui peut être utile dans les cas avancés. Pour HTTP, cela sera défini à la fin. Dans Angular, il peut être utile dans certaines situations de définir une propriété _isDestroyed dans ngDestroy pouvant être vérifiée par votre gestionnaire subscribe.

Conseil n ° 2: si vous gérez plusieurs abonnements, vous pouvez créer un objet new Subscription() et add(...) ad-hoc.

50
Simon_Weaver

L'appel de la méthode unsubscribe consiste plutôt à annuler une requête HTTP en cours, car elle appelle la méthode abort sur l'objet XHR sous-jacent et supprime les écouteurs lors des événements de chargement et d'erreur:

// From the XHRConnection class
return () => {
  _xhr.removeEventListener('load', onLoad);
  _xhr.removeEventListener('error', onError);
  _xhr.abort();
};

Ceci dit, unsubscribe supprime les auditeurs ... Cela pourrait donc être une bonne idée mais je ne pense pas que ce soit nécessaire pour une seule demande ;-)

J'espère que ça vous aide, Thierry

21
Thierry Templier

Aussi avec le nouveau module HttpClient, reste le même comportement packages/common/http/src/jsonp.ts

11
Ricardo Martínez

Vous ne devez pas vous désabonner des observables qui se terminent automatiquement (par exemple, Http, appels). Mais il est nécessaire de se désabonner des observables infinies telles que Observable.timer().

8
Misha Mykhalyuk

Vous devriez certainement lire this article. Il vous montre pourquoi vous devriez toujours vous désabonner même de http .

Si, après avoir créé la demande mais avant de recevoir une réponse du back-end, vous considérez que le composant est inutile et le détruisez, votre abonnement conservera la référence au composant, créant ainsi une chance de provoquer des fuites de mémoire.

Mettre à jour

L'affirmation ci-dessus semble être vraie, mais quand même, quand la réponse nous parviendra, l'abonnement http sera détruit de toute façon.

2
MTZ

Se désabonner est un DOIT si vous voulez un comportement déterministe à toutes les vitesses du réseau.

Imagine le composant A est rendu dans un onglet - Vous cliquez sur un bouton pour envoyer une demande 'GET'. Il faut 200 ms pour que la réponse revienne. Vous pouvez donc fermer l’onglet à tout moment en sachant que la machine sera plus rapide que la réponse http; elle est traitée et terminée avant la fermeture de l’onglet et la destruction du composant A.

pourquoi pas sur un réseau très lent? Vous cliquez sur un bouton. La requête "GET" met 10 secondes pour recevoir sa réponse, mais 5 secondes vous attendent pour décider de fermer l'onglet. Cela détruira le composant A qui sera ramassé ultérieurement. Attendez une minute!, nous ne nous sommes pas désabonnés - maintenant 5 secondes plus tard, une réponse est renvoyée et la logique du composant détruit sera exécutée. Cette exécution est maintenant considérée comme out-of-context et peut avoir de nombreuses conséquences, notamment une performance très faible.

La meilleure pratique consiste donc à utiliser takeUntil() et à vous désabonner des appels http lorsque le composant est détruit.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

interface User {
  id: string;
  name: string;
  age: number;
}

@Component({
  selector: 'app-foobar',
  templateUrl: './foobar.component.html',
  styleUrls: ['./foobar.component.scss'],
})
export class FoobarComponent implements OnInit, OnDestroy {
  private user: User = null;
  private destroy$ = new Subject();

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http
      .get<User>('api/user/id')
      .pipe(takeUntil(this.destroy$))
      .subscribe(user => {
        this.user = user;
      });
  }

  ngOnDestroy(): void {
    this.destroy$.next();  // trigger the unsubscribe
    this.destroy$.complete(); // finalize & clean up the subject stream
  }
}
0
un33k