--- Edit 4 - Ressources supplémentaires (2018/09/01)
Dans un récent épisode de Aventures en Angular Ben Lesh et Ward Bell discutent des questions relatives à la manière et au moment de se désabonner dans un composant. La discussion commence à environ 1:05:30.
Ward mentionne right now there's an awful takeUntil dance that takes a lot of machinery
et Shai Reznik mentionne Angular handles some of the subscriptions like http and routing
.
En réponse, Ben mentionne qu'il y a actuellement des discussions pour permettre aux Observables de s'accrocher aux événements du cycle de vie des composants Angular et Ward suggère un Observable des événements du cycle de vie auquel un composant pourrait s'abonner comme moyen de savoir quand compléter les Observables maintenus comme état interne du composant.
Cela dit, nous avons surtout besoin de solutions maintenant, alors voici d'autres ressources.
-
Une recommandation pour le takeUntil()
de Nicholas Jamieson, membre de l'équipe centrale de RxJs, et une règle tslint pour aider à la faire respecter. https://ncjamieson.com/avoiding-takeuntil-leaks/
-
Paquet npm léger qui expose un opérateur Observable qui prend une instance de composant ( this
) en tant que paramètre et se désinscrit automatiquement pendant ngOnDestroy
. https://github.com/NetanelBasal/ngx-take-until-destroy
-
Une autre variation de la précédente avec une ergonomie légèrement meilleure si vous ne faites pas de constructions AOT (mais nous devrions tous faire des AOT maintenant). https://github.com/smnbbrv/ngx-rx-collector
-
Directive personnalisée *ngSubscribe
qui fonctionne comme async pipe mais crée une vue intégrée dans votre modèle afin que vous puissiez vous référer à la valeur "unwrapped" dans votre modèle. https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
Je mentionne dans un commentaire sur le blog de Nicholas que la surutilisation des takeUntil()
pourrait être un signe que votre composant essaie d'en faire trop et que la séparation de vos composants existants en Fonctionnalité y Présentation doivent être pris en compte. Vous pouvez alors | async
l'Observable du composant Feature dans un Input
du composant "Presentational", ce qui signifie qu'aucun abonnement n'est nécessaire nulle part. En savoir plus sur cette approche aquí
--- Edit 3 - La solution "officielle" (2017/04/09)
J'ai discuté de cette question avec Ward Bell à la NGConf (je lui ai même montré cette réponse qui, selon lui, était correcte) mais il m'a dit que l'équipe chargée de la documentation d'Angular avait une solution à cette question qui n'est pas publiée (bien qu'elle travaille à la faire approuver). Il m'a également dit que je pouvais mettre à jour ma réponse SO avec la recommandation officielle à venir.
La solution que nous devrions tous utiliser à l'avenir est d'ajouter une fonction private ngUnsubscribe = new Subject();
à tous les composants qui ont .subscribe()
appels à Observable
dans leur code de classe.
Nous appelons alors this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();
dans notre ngOnDestroy()
méthodes.
La sauce secrète (comme déjà noté par @metamaker ) est d'appeler takeUntil(this.ngUnsubscribe)
avant chacun de nos .subscribe()
qui garantiront que tous les abonnements seront nettoyés lorsque le composant sera détruit.
Exemple :
import { Component, OnDestroy, OnInit } from '@angular/core';
// RxJs 6.x+ import paths
import { filter, startWith, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { BookService } from '../books.service';
@Component({
selector: 'app-books',
templateUrl: './books.component.html'
})
export class BooksComponent implements OnDestroy, OnInit {
private ngUnsubscribe = new Subject();
constructor(private booksService: BookService) { }
ngOnInit() {
this.booksService.getBooks()
.pipe(
startWith([]),
filter(books => books.length > 0),
takeUntil(this.ngUnsubscribe)
)
.subscribe(books => console.log(books));
this.booksService.getArchivedBooks()
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(archivedBooks => console.log(archivedBooks));
}
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}
Nota: Il est important d'ajouter le takeUntil
comme le dernier opérateur pour éviter les fuites avec les observables intermédiaires dans la chaîne d'opérateurs.
--- Edit 2 (2016/12/28)
Source 5
Dans le tutoriel Angular, le chapitre sur le routage indique désormais ce qui suit : " Le routeur gère les observables qu'il fournit et localise les abonnements. Les abonnements sont nettoyés lorsque le composant est détruit, ce qui protège contre les fuites de mémoire, de sorte que nous n'avons pas besoin de nous désabonner de l'Observable des paramètres de route." - Mark Rajcok
Voici un discussion sur les questions Github pour les documents Angular concernant Router Observables où Ward Bell mentionne que la clarification de tout cela est en cours.
--- Edit 1
Source 4
Dans ce vidéo de NgEurope Rob Wormald affirme également qu'il n'est pas nécessaire de se désabonner de Router Observables. Il mentionne également le http
service et ActivatedRoute.params
dans ce vidéo de novembre 2016 .
--- Réponse originale
TLDR :
Pour cette question, il y a (2) sortes de Observables
- finie valeur et infini valeur.
http
Observables
produire finie (1) des valeurs et quelque chose comme un DOM event listener
Observables
produire infini valeurs.
Si vous appelez manuellement subscribe
(sans utiliser de pipe asynchrone), alors unsubscribe
de infini Observables
.
Ne vous inquiétez pas pour finie les uns et les autres, RxJs
s'occupera d'eux.
Fuente 1
J'ai trouvé une réponse de Rob Wormald dans le Gitter d'Angular. aquí .
Il déclare (j'ai réorganisé pour la clarté et l'emphase est la mienne)
si son une séquence à valeur unique (comme une requête http) le site le nettoyage manuel est inutile (en supposant que vous vous inscrivez manuellement dans le contrôleur)
Je devrais dire "si c'est un séquence qui complète " (dont les séquences à valeur unique, à la http, sont une)
si c'est une séquence infinie , vous devez vous désinscrire ce que le tuyau asynchrone fait pour vous
Il mentionne également dans ce vidéo youtube sur les observables qui they clean up after themselves
... dans le contexte des Observables qui complete
(comme les promesses, qui sont toujours complètes parce qu'elles produisent toujours une valeur et se terminent - nous ne nous sommes jamais inquiétés de nous désabonner des promesses pour nous assurer qu'elles se nettoient. xhr
écouteurs d'événements, n'est-ce pas ?).
Fuente 2
Également dans le Guide de Rangle sur Angular 2 On peut y lire
Dans la plupart des cas, nous n'aurons pas besoin d'appeler explicitement la méthode de désabonnement, à moins que nous ne voulions résilier prématurément ou que notre Observable ait une durée de vie supérieure à celle de notre abonnement. Le comportement par défaut des opérateurs d'Observable est de se débarrasser de l'abonnement dès que les messages .complete() ou .error() sont publiés. Gardez à l'esprit que RxJS a été conçu pour être utilisé la plupart du temps selon le principe du "feu et oublie".
Quand est-ce que la phrase our Observable has a longer lifespan than our subscription
appliquer ?
Il s'applique lorsqu'un abonnement est créé à l'intérieur d'un composant qui est détruit avant (ou pas "longtemps" avant) la date limite d'utilisation de l'abonnement. Observable
complète.
J'interprète cela comme signifiant que si nous souscrivons à un http
ou un observable qui émet 10 valeurs et que notre composant est détruit avant cela. http
revient ou les 10 valeurs ont été émises, nous sommes toujours ok !
Lorsque la requête est retournée ou que la 10e valeur est finalement émise, l'option Observable
se terminera et toutes les ressources seront nettoyées.
Source 3
Si nous regardons cet exemple à partir du même guide Rangle, nous pouvons voir que le Subscription
a route.params
nécessite un unsubscribe()
parce que nous ne savons pas quand ces params
cessera de changer (en émettant de nouvelles valeurs).
Le composant pourrait être détruit en quittant la navigation, auquel cas les paramètres de la route continueront probablement à changer (ils pourraient techniquement changer jusqu'à ce que l'application se termine) et les ressources allouées dans la souscription seront toujours allouées parce qu'il n'y a pas eu de completion
.
33 votes
Je suppose
Subscription
s àhttp-requests
peuvent être ignorés, car ils n'appellent queonNext
une fois et ensuite ils appellentonComplete
. Le siteRouter
appelle plutôtonNext
à plusieurs reprises et pourrait ne jamais appeleronComplete
(je n'en suis pas sûr...). Il en va de même pourObservable
s deEvent
s. Donc je suppose qu'ils devraient êtreunsubscribed
.1 votes
@gt6707a Le flux se termine (ou ne se termine pas) indépendamment de toute observation de cet achèvement. Les rappels (l'observateur) fournis à la fonction d'abonnement ne déterminent pas si les ressources sont allouées. C'est l'appel à
subscribe
lui-même qui alloue potentiellement des ressources en amont.1 votes
Faites-en un
muscle memory
de vous désabonner explicitement dans votretypescript
. Même lehttp
abonnements. Ex : AHttp.get()
complète la réponse. Si l'api de votre serveur prend10 seconds
pour répondre, et votre composant est détruit dans5 seconds
de l'appel, votre réponse arrivera5 seconds
after
la destruction du composant. Cela déclenchera une exécution hors-contexte, ce qui est bien pire que la partie fuite de mémoire, indiquée dans les docs d'Angular.1 votes
@unk33k pouvez-vous partager le lien exact vers la documentation ? Désolé, je n'arrive pas à trouver ce passage.