--- Edit 4 - Ressources supplémentaires (2018/09/01)
Dans un récent épisode d' Aventures in Angular, Ben Lesh et Ward Bell discutent des questions concernant comment / quand se désinscrire dans un composant. La discussion commence vers 13 h 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 des discussions en ce moment pour permettre aux observables de se connecter aux événements du cycle de vie du composant angulaire et Ward suggère un observable des événements du cycle de vie auquel un composant pourrait souscrire pour savoir quand terminer les observables maintenus en tant qu'état interne du composant.
Cela dit, nous avons surtout besoin de solutions maintenant, voici donc d'autres ressources.
Une recommandation pour le takeUntil()
modèle du membre de l'équipe principale de RxJs Nicholas Jamieson et une règle tslint pour aider à l'appliquer. https://ncjamieson.com/avoiding-takeuntil-leaks/
Package npm léger qui expose un opérateur Observable qui prend une instance de composant ( this
) comme paramètre et se désabonne automatiquement pendant ngOnDestroy
.
https://github.com/NetanelBasal/ngx-take-until-destroy
Une autre variante de ce qui précède avec une ergonomie légèrement meilleure si vous ne faites pas de builds AOT (mais nous devrions tous le faire maintenant).
https://github.com/smnbbrv/ngx-rx-collector
Directive personnalisée *ngSubscribe
qui fonctionne comme un canal asynchrone mais crée une vue intégrée dans votre modèle afin que vous puissiez vous référer à la valeur «non encapsulée» dans votre modèle.
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
Je mentionne dans un commentaire au blog de Nicholas que la surutilisation de takeUntil()
pourrait être un signe que votre composant essaie d'en faire trop et que la séparation de vos composants existants en composants de fonctionnalité et de présentation devrait être envisagée. Vous pouvez ensuite | async
l'Observable à partir du composant Feature dans un Input
composant Presentational, ce qui signifie qu'aucun abonnement n'est nécessaire n'importe où. En savoir plus sur cette approche ici
--- Edit 3 - La solution «officielle» (2017/04/09)
J'ai parlé à Ward Bell de cette question à NGConf (je lui ai même montré cette réponse qu'il a dite correcte) mais il m'a dit que l'équipe de documentation pour Angular avait une solution à cette question qui n'est pas publiée (bien qu'ils travaillent à la faire approuver) ). Il m'a également dit que je pouvais mettre à jour ma réponse SO avec la prochaine recommandation officielle.
La solution que nous devrions tous utiliser à l'avenir est d'ajouter un private ngUnsubscribe = new Subject();
champ à tous les composants qui ont des .subscribe()
appels à Observable
s dans leur code de classe.
Nous appelons ensuite this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();
nos ngOnDestroy()
méthodes.
La sauce secrète (comme déjà noté par @metamaker ) est d'appeler takeUntil(this.ngUnsubscribe)
avant chacun de nos .subscribe()
appels qui garantira 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();
}
}
Remarque: Il est important d'ajouter l' takeUntil
opérateur comme dernier pour éviter les fuites avec des observables intermédiaires dans la chaîne de l'opérateur.
--- Modifier 2 (28/12/2016)
Source 5
Le tutoriel Angular, le chapitre Routage indique maintenant 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, protégeant contre les fuites de mémoire, nous n'avons donc pas besoin de vous désabonner de la route params Observable. " - Mark Rajcok
Voici une discussion sur les problèmes de Github pour les documents Angular concernant Router Observables où Ward Bell mentionne qu'une clarification pour tout cela est en cours.
--- Modifier 1
Source 4
Dans cette vidéo de NgEurope, Rob Wormald dit également que vous n'avez pas besoin de vous désabonner de Router Observables. Il mentionne également le http
service et ActivatedRoute.params
dans cette vidéo de novembre 2016 .
--- Réponse originale
TLDR:
Pour cette question, il existe (2) sortes de Observables
- valeur finie et valeur infinie .
http
Observables
produire des valeurs finies (1) et quelque chose comme un DOM event listener
Observables
produire des valeurs infinies .
Si vous appelez manuellement subscribe
(sans utiliser de canal asynchrone), alors unsubscribe
depuis l' infini Observables
.
Ne vous inquiétez pas des finis , RxJs
prenez soin d'eux.
Source 1
J'ai retrouvé une réponse de Rob Wormald dans Angular's Gitter ici .
Il déclare (j'ai réorganisé pour plus de clarté et l'accent est à moi)
s'il s'agit d' une séquence à valeur unique (comme une requête http), le nettoyage manuel n'est pas nécessaire (en supposant que vous vous abonnez manuellement dans le contrôleur)
je devrais dire "si c'est une séquence qui se termine " (dont les séquences à valeur unique, à la http, en sont une)
si c'est une séquence infinie , vous devez vous désabonner de ce que le canal asynchrone fait pour vous
Il mentionne également dans cette vidéo YouTube sur Observables que they clean up after themselves
... dans le contexte d'Observables qui complete
(comme Promises, qui se terminent toujours parce qu'ils produisent toujours 1 valeur et se terminant - nous ne nous sommes jamais inquiétés de la désinscription de Promises pour nous assurer qu'ils nettoient l' xhr
événement auditeurs, non?).
Source 2
Également dans le guide Rangle d'Angular 2, il lit
Dans la plupart des cas, nous n'aurons pas besoin d'appeler explicitement la méthode de désabonnement, sauf si nous voulons annuler tôt ou que notre observable a une durée de vie plus longue que notre abonnement. Le comportement par défaut des opérateurs observables consiste à supprimer l'abonnement dès que les messages .complete () ou .error () sont publiés. Gardez à l'esprit que le RxJS a été conçu pour être utilisé la plupart du temps de façon "feu et oubliez".
Quand la phrase our Observable has a longer lifespan than our subscription
s'applique-t-elle?
Elle s'applique lorsqu'un abonnement est créé à l'intérieur d'un composant qui est détruit avant (ou pas «longtemps» avant) la Observable
fin.
Je lis cela comme signifiant que si nous souscrivons à une http
demande ou à un observable qui émet 10 valeurs et que notre composant est détruit avant que cette http
demande ne revienne ou que les 10 valeurs aient été émises, nous sommes toujours d'accord!
Lorsque la demande revient ou que la 10e valeur est finalement émise, la tâche Observable
se termine et toutes les ressources sont nettoyées.
Source 3
Si nous regardons cet exemple dans le même guide Rangle, nous pouvons voir que le Subscription
to route.params
nécessite un unsubscribe()
car nous ne savons pas quand ceux- params
ci cesseront de changer (en émettant de nouvelles valeurs).
Le composant pourrait être détruit en s'éloignant, auquel cas les paramètres d'itinéraire continueront probablement de changer (ils pourraient techniquement changer jusqu'à la fin de l'application) et les ressources allouées en abonnement seraient toujours allouées car il n'y en a pas eu completion
.
Subscription
shttp-requests
peut être ignoré, car ils n'appellentonNext
qu'une seule fois puis ils appellentonComplete
. LaRouter
place appelle àonNext
plusieurs reprises et pourrait ne jamais appeleronComplete
(pas sûr à ce sujet ...). Il en va de même pourObservable
s deEvent
s. Donc je suppose que ça devrait l'êtreunsubscribed
.