Angular / RxJs Quando dovrei annullare l’iscrizione da `Abbonamento`

Quando dovrei memorizzare le istanze di Subscription e invocare unsubscribe() durante il ciclo di vita di NgOnDestroy e quando posso semplicemente ignorarle?

Il salvataggio di tutti gli abbonamenti introduce un sacco di problemi nel codice componente.

La guida client HTTP ignora le iscrizioni in questo modo:

 getHeroes() { this.heroService.getHeroes() .subscribe( heroes => this.heroes = heroes, error => this.errorMessage = error); } 

Allo stesso tempo, la Guida di navigazione e itinerario dice che:

Alla fine, navigheremo da qualche altra parte. Il router rimuoverà questo componente dal DOM e lo distruggerà. Abbiamo bisogno di ripulire noi stessi prima che ciò accada. Nello specifico, dobbiamo annullare l’iscrizione prima che Angular distrugga il componente. In caso contrario, potrebbe verificarsi una perdita di memoria.

ngOnDestroy l’ ngOnDestroy al ngOnDestroy metodo Observable nel metodo ngOnDestroy .

 private sub: any; ngOnInit() { this.sub = this.route.params.subscribe(params => { let id = +params['id']; // (+) converts string 'id' to a number this.service.getHero(id).then(hero => this.hero = hero); }); } ngOnDestroy() { this.sub.unsubscribe(); } 

— Modifica 4 – Risorse aggiuntive (2018/09/01)

In un recente episodio di Adventures in Angular, Ben Lesh e Ward Bell discutono i problemi relativi a come / quando annullare l’iscrizione a un componente. La discussione inizia alle 1:05:30.

Ward dice che in right now there's an awful takeUntil dance that takes a lot of machinery e Shai Reznik menziona Angular handles some of the subscriptions like http and routing .

In risposta Ben ricorda che in questo momento ci sono discussioni per consentire agli osservabili di collegarsi agli eventi del ciclo di vita dei componenti angolari e Ward suggerisce un Osservabile degli eventi del ciclo di vita che un componente potrebbe sottoscrivere come un modo per sapere quando completare gli osservabili mantenuti come stato interno dei componenti.

Detto questo, per lo più abbiamo bisogno di soluzioni ora, quindi ecco alcune altre risorse.

  1. Una raccomandazione per il modello takeUntil() dal membro del team principale di RxJs Nicholas Jamieson e una regola di tslint per aiutare a rafforzarla. https://blog.angularindepth.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef

  2. Pacchetto npm leggero che espone un operatore Observable che prende un’istanza di componente ( this ) come parametro e ngOnDestroy automaticamente l’annullamento durante ngOnDestroy . https://github.com/NetanelBasal/ngx-take-until-destroy

  3. Un’altra variante di quanto sopra con un’ergonomia leggermente migliore se non stai facendo build AOT (ma dovremmo fare tutti AOT ora). https://github.com/smnbbrv/ngx-rx-collector

  4. Direttiva personalizzata *ngSubscribe che funziona come pipe asincrono ma crea una vista incorporata nel modello in modo da poter fare riferimento al valore ‘unwrapped’ in tutto il modello. https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f

takeUntil() in un commento al blog di Nicholas che l’uso takeUntil() di takeUntil() potrebbe essere un segnale che il componente sta cercando di fare troppo e che la separazione dei componenti esistenti in componenti Feature e Presentational dovrebbe essere presa in considerazione. Puoi quindi | async | async il componente Osservabile dal Feature in un Input del componente Presentational, il che significa che non sono necessarie iscrizioni ovunque. Leggi di più su questo approccio qui

— Modifica 3 – La soluzione ‘ufficiale’ (2017/04/09)

Ho parlato con Ward Bell di questa domanda a NGConf (gli ho persino mostrato questa risposta che ha detto fosse corretta) ma mi ha detto che il team di doc per Angular aveva una soluzione a questa domanda che non è stata pubblicata (sebbene stiano lavorando per ottenerlo approvato ). Mi ha anche detto che potrei aggiornare la mia risposta SO con la prossima raccomandazione ufficiale.

La soluzione che dovremmo usare in futuro è aggiungere un private ngUnsubscribe = new Subject(); campo a tutti i componenti che hanno .subscribe() chiama Observable s all’interno del loro codice di class.

Quindi chiamiamo this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); nei nostri metodi ngOnDestroy() .

La salsa segreta (come già notato da @metamaker ) consiste nel chiamare takeUntil(this.ngUnsubscribe) prima di ciascuna delle nostre chiamate .subscribe() che garantirà che tutte le sottoscrizioni vengano ripulite quando il componente viene distrutto.

Esempio:

 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: è importante aggiungere l’operatore takeUntil come ultimo per evitare perdite con osservabili intermedi nella catena dell’operatore.

— Modifica 2 (28/12/2016)

Fonte 5

Il tutorial Angular, il capitolo Routing ora afferma quanto segue: “Il Router gestisce le osservabili che fornisce e localizza le sottoscrizioni. Le sottoscrizioni vengono ripulite quando il componente viene distrutto, proteggendo da perdite di memoria, quindi non è necessario annullare l’iscrizione da il percorso params Observable. ” – Mark Rajcok

Ecco una discussione sui problemi di Github per i documenti angolari relativi a Router Observables, dove Ward Bell menziona che il chiarimento di tutto questo è in corso.

— Modifica 1

Fonte 4

In questo video di NgEurope, Rob Wormald afferma anche che non è necessario annullare l’iscrizione a Router Observables. Menziona anche il servizio http e ActivatedRoute.params in questo video da novembre 2016 .

— Risposta originale

TLDR:

Per questa domanda ci sono (2) tipi di Observables – valore finito e valore infinito .

http Observables produce valori finiti (1) e qualcosa come un event listener DOM Gli Observables producono valori infiniti .

Se si chiama subscribe manualmente (non si utilizza la pipe asincrona), si unsubscribe da Observables infiniti .

Non preoccuparti di quelli finiti , gli RxJs si prenderanno cura di loro.

Fonte 1

Ho rintracciato qui una risposta di Rob Wormald in Angular’s Gitter.

Dichiara (mi sono riorganizzato per chiarezza e l’enfasi è mia)

se è una sequenza a valore singolo (come una richiesta http) la pulizia manuale non è necessaria (presupponendo che si iscriva manualmente nel controller)

dovrei dire “se è una sequenza che completa ” (di cui le sequenze a valore singolo, a la http, sono una sola)

se è una sequenza infinita , è necessario annullare l’ iscrizione che il tubo asincrono fa per te

Inoltre menziona in questo video di YouTube su Observables che they clean up after themselves … nel contesto degli Observables che complete (come Promises, che si completano sempre perché producono sempre un valore e finiscono) non ci siamo mai preoccupati di annullare l’iscrizione da Promises a assicurati che xhr ascoltatori di eventi xhr , giusto?).

Fonte 2

Anche nella guida di Rangle per Angular 2 si legge

Nella maggior parte dei casi non sarà necessario chiamare esplicitamente il metodo di annullamento della sottoscrizione, a meno che non desideriamo annullare anticipatamente o che il nostro Osservabile abbia una durata maggiore rispetto al nostro abbonamento. Il comportamento predefinito degli operatori Observable è quello di eliminare la sottoscrizione non appena vengono pubblicati i messaggi .complete () o .error (). Tieni presente che RxJS è stato progettato per essere utilizzato in modo “fuoco e dimentica” la maggior parte del tempo.

Quando la frase ” our Observable has a longer lifespan than our subscription ?

Si applica quando viene creata una sottoscrizione all’interno di un componente che viene distrutto prima (o non “lungo” prima) che l’ Observable completa.

Leggo questo nel senso che se ci iscriviamo ad una richiesta http o ad un osservabile che emette 10 valori e il nostro componente viene distrutto prima che la richiesta http ritorni o siano stati emessi i 10 valori, siamo ancora ok!

Quando la richiesta viene restituita o il decimo valore viene finalmente emesso, l’ Observable verrà completato e tutte le risorse verranno ripulite.

Fonte 3

Se guardiamo questo esempio dalla stessa guida di Rangle possiamo vedere che l’ Subscription a route.params richiede un unsubscribe() perché non sappiamo quando quei params smetteranno di cambiare (emettendo nuovi valori).

Il componente potrebbe essere distrutto navigando lontano, nel qual caso i parametri del percorso probabilmente cambieranno (potrebbero cambiare tecnicamente fino alla fine dell’app) e le risorse allocate nell’abbonamento saranno ancora allocate perché non è stato completion .

Non è necessario avere un sacco di abbonamenti e disiscriversi manualmente. Usa RxJS.Subject e takeUntil combo per gestire abbonamenti come un boss:

 import {Subject} from "rxjs/Subject"; @Component( { moduleId: __moduleName, selector: 'my-view', templateUrl: '../views/view-route.view.html', } ) export class ViewRouteComponent implements OnDestroy { componentDestroyed$: Subject = new Subject(); constructor(protected titleService: TitleService) { this.titleService.emitter1$ .takeUntil(this.componentDestroyed$) .subscribe( (data: any) => { // ... do something 1 } ); this.titleService.emitter2$ .takeUntil(this.componentDestroyed$) .subscribe( (data: any) => { // ... do something 2 } ); // ... this.titleService.emitterN$ .takeUntil(this.componentDestroyed$) .subscribe( (data: any) => { // ... do something N } ); } ngOnDestroy() { this.componentDestroyed$.next(true); this.componentDestroyed$.complete(); } } 

L’approccio alternativo , che è stato proposto da @acumartini nei commenti , utilizza takeWhile anziché takeUntil . Potresti preferirlo, ma ricorda che in questo modo la tua esecuzione osservabile non verrà annullata su ngDestroy del tuo componente (ad esempio quando esegui calcoli che richiedono molto tempo o attendi dati dal server). Il metodo, che è basato su takeUntil , non ha questo inconveniente e porta all’annullamento immediato della richiesta. Grazie a @AlexChe per la spiegazione dettagliata nei commenti .

Quindi ecco il codice:

 @Component( { moduleId: __moduleName, selector: 'my-view', templateUrl: '../views/view-route.view.html', } ) export class ViewRouteComponent implements OnDestroy { alive: boolean = true; constructor(protected titleService: TitleService) { this.titleService.emitter1$ .takeWhile(() => this.alive) .subscribe( (data: any) => { // ... do something 1 } ); this.titleService.emitter2$ .takeWhile(() => this.alive) .subscribe( (data: any) => { // ... do something 2 } ); // ... this.titleService.emitterN$ .takeWhile(() => this.alive) .subscribe( (data: any) => { // ... do something N } ); } // Probably, this.alive = false MAY not be required here, because // if this.alive === undefined, takeWhile will stop. I // will check it as soon, as I have time. ngOnDestroy() { this.alive = false; } } 

La class Subscription ha una caratteristica interessante:

Rappresenta una risorsa usa e getta, come l’esecuzione di un Osservabile. Un abbonamento ha un metodo importante, annullare l’iscrizione, che non accetta argomenti e dispone solo della risorsa detenuta dall’abbonamento.
Inoltre, le sottoscrizioni possono essere raggruppate insieme attraverso il metodo add (), che allegherà una Sottoscrizione subordinata alla Sottoscrizione corrente. Quando un abbonamento è annullato, tutti i suoi figli (e i suoi nipoti) saranno annullati.

È ansible creare un object di sottoscrizione aggregato che raggruppa tutte le sottoscrizioni. A tale scopo, crea una sottoscrizione vuota e aggiungi abbonamenti utilizzando il suo metodo add() . Quando il componente viene distrutto, è necessario solo annullare l’iscrizione alla sottoscrizione aggregata.

 @Component({ ... }) export class SmartComponent implements OnInit, OnDestroy { private subscriptions = new Subscription(); constructor(private heroService: HeroService) { } ngOnInit() { this.subscriptions.add(this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes)); this.subscriptions.add(/* another subscription */); this.subscriptions.add(/* and another subscription */); this.subscriptions.add(/* and so on */); } ngOnDestroy() { this.subscriptions.unsubscribe(); } } 

Dipende. Se si chiama someObservable.subscribe() , si inizia a contenere alcune risorse che devono essere liberate manualmente quando il ciclo di vita del componente è finito, quindi si dovrebbe chiamare theSubscription.unsubscribe() per evitare perdite di memoria.

Diamo un’occhiata più da vicino ai tuoi esempi:

getHero() restituisce il risultato di http.get() . Se si guarda il codice sorgente angular 2, http.get() crea due listener di eventi:

 _xhr.addEventListener('load', onLoad); _xhr.addEventListener('error', onError); 

e chiamando unsubscribe() , è ansible annullare la richiesta così come gli ascoltatori:

 _xhr.removeEventListener('load', onLoad); _xhr.removeEventListener('error', onError); _xhr.abort(); 

Nota che _xhr è specifico per la piattaforma, ma penso che sia sicuro assumere che sia XMLHttpRequest() nel tuo caso.

Normalmente, questa è una prova sufficiente per giustificare una chiamata di unsubscribe() manuale unsubscribe() . Ma secondo questa specifica WHATWG , il XMLHttpRequest() è sobject alla garbage collection una volta che è “finito”, anche se ci sono listener di eventi collegati ad esso. Quindi suppongo che è per questo che la guida ufficiale di angular 2 omette l’ unsubscribe() dell’iscrizione unsubscribe() e consente a GC di ripulire gli ascoltatori.

Per quanto riguarda il tuo secondo esempio, dipende dall’implementazione dei params . A partire da oggi, la guida angular ufficiale non mostra più l’annullamento params dai params . Ho controllato di nuovo src e ho scoperto che params è solo un BehaviorSubject . Poiché non sono stati utilizzati listener o timer di eventi e non sono state create variabili globali, è opportuno omettere l’ unsubscribe() .

La linea di fondo per la tua domanda è che chiama sempre unsubscribe() come guardia contro la perdita di memoria, a meno che tu non sia certo che l’esecuzione dell’osservabile non crei variabili globali, aggiungi ascoltatori di eventi, imposta timer o faccia qualsiasi altra cosa che risulta in perdite di memoria.

In caso di dubbio, esamina l’implementazione di tale osservabile. Se l’osservabile ha scritto una logica di ripulitura nella sua unsubscribe() , che di solito è la funzione restituita dal costruttore, allora hai buone ragioni per prendere seriamente in considerazione la possibilità di chiamare unsubscribe() .

Alcune delle migliori pratiche relative alle disiscrizioni osservabili all’interno dei componenti angolari:

Una citazione da Routing & Navigation

Quando ti iscrivi a un osservabile in un componente, quasi sempre prevedi di annullare l’iscrizione quando il componente viene distrutto.

Ci sono alcuni osservabili eccezionali dove questo non è necessario. Le osservabili di ActivatedRoute sono tra le eccezioni.

ActivatedRoute e i suoi osservabili sono isolati dal router stesso. Il router distrugge un componente instradato quando non è più necessario e l’ActivatedRoute iniettato muore con esso.

Sentiti libero di disiscriverti comunque. È innocuo e mai una ctriggers pratica.

E nel rispondere ai seguenti link:

  • (1) Devo annullare l’iscrizione ad Osservable Http Angular 2?
  • (2) È necessario annullare l’iscrizione agli osservabili creati con i metodi Http?
  • (3) RxJS: non annullare l’iscrizione
  • (4) Il modo più semplice per annullare l’iscrizione a Observable in Angular
  • (5) Documentazione per RxJS annullamento dell’iscrizione
  • (6) L’ annullamento dell’iscrizione in un servizio è un po ‘inutile poiché non ci sono possibilità di perdite di memoria
  • (7) Abbiamo bisogno di disiscriversi da osservabili che completano / errori?
  • (8) Un commento sull’osservabile http

Ho raccolto alcune delle migliori pratiche relative alle disiscrizioni osservabili all’interno di componenti Angolari da condividere con voi:

  • http disiscrizione http osservabile è condizionale e dovremmo considerare gli effetti del “callback di sottoscrizione” eseguito dopo che il componente è stato distrutto caso per caso. Sappiamo che l’angular disiscrive e pulisce l’osservabile http stesso (1) , (2) . Mentre questo è vero dal punto di vista delle risorse, dice solo metà della storia. Diciamo che stiamo parlando di chiamare direttamente http da un componente, e la risposta http richiesto più tempo del necessario, quindi l’utente ha chiuso il componente. Il gestore subscribe() verrà comunque chiamato anche se il componente è chiuso e distrutto. Questo può avere effetti collaterali indesiderati e negli scenari peggiori lasciare lo stato dell’applicazione rotto. Può anche causare eccezioni se il codice nel callback tenta di chiamare qualcosa che è stato appena eliminato. Tuttavia allo stesso tempo occasionalmente sono desiderati. Ad esempio, supponiamo che tu stia creando un client di posta elettronica e attivi un suono quando l’e-mail ha terminato l’invio – beh, vorresti che succedesse anche se il componente è chiuso ( 8 ).
  • Non è necessario annullare l’iscrizione agli osservabili che completano o all’errore. Tuttavia, non c’è nulla di male nel farlo (7) .
  • Usa AsyncPipe il più ansible perché si AsyncPipe automaticamente dalla distruzione osservabile del componente.
  • Annulla l’iscrizione agli route.params osservabili di ActivatedRoute come route.params se sono iscritti all’interno di un nidificato (aggiunto all’interno di tpl con il selettore di componenti) o di un componente dinamico in quanto possono essere sottoscritti molte volte finché esiste il componente principale / host. Non è necessario annullare l’iscrizione da loro in altri scenari come menzionato nella citazione sopra dai documenti di Routing & Navigation .
  • Annullare l’iscrizione agli osservabili globali condivisi tra componenti esposti tramite un servizio Angular, ad esempio poiché possono essere sottoscritti più volte fintanto che il componente viene inizializzato.
  • Non è necessario annullare l’iscrizione agli osservabili interni del servizio poiché un servizio Angular non viene mai distrutto, a meno che l’intera applicazione non venga distrutta, non vi è alcun motivo valido per annullare l’iscrizione e non vi è alcuna possibilità di perdite di memoria. (6)
  • Utilizzare una tecnica astratta per evitare qualsiasi problema di codice che potrebbe derivare da disiscrizioni. Puoi gestire i tuoi abbonamenti con takeUntil (3) o puoi usare questo pacchetto npm cui al punto (4) Il modo più semplice per annullare l’iscrizione a Observables in Angular .
  • FormGroup sempre l’iscrizione FormGroup osservabili di form.valueChanges come form.valueChanges e form.statusChanges
  • Annullare sempre l’iscrizione agli osservabili del servizio Renderer2 come renderer2.listen
  • Annullare l’iscrizione ad ogni altro osservabile come un passaggio di protezione della perdita di memoria fino a quando Angular Docs non ci dirà esplicitamente quali osservazioni non sono necessarie per essere annullate (Verifica problema: (5) Documentazione per RxJS Annullamento sottoscrizione (Aperto) ).
  • Bonus: utilizzare sempre le modalità Angolare per associare eventi come HostListener alle preoccupazioni angolari sulla rimozione degli ascoltatori di eventi, se necessario, e prevenire potenziali perdite di memoria dovute ai binding di eventi.

Un buon consiglio finale : se non sai se un osservabile viene automaticamente annullato / completato o meno, aggiungi un callback complete per subscribe(...) e controlla se viene chiamato quando il componente viene distrutto.

La documentazione ufficiale di Angular 2 fornisce una spiegazione su quando annullare l’iscrizione e quando può essere tranquillamente ignorata. Dai un’occhiata a questo link:

https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

Cerca il paragrafo con l’intestazione Parent e children comunicano tramite un servizio e poi la casella blu:

Si noti che acquisiamo la sottoscrizione e annulliamo l’iscrizione quando DistronautComponent viene distrutto. Questo è un passo per la perdita di memoria. Non ci sono rischi reali in questa app perché la durata di un AstronautComponent è uguale alla durata della stessa app. Ciò non sarebbe sempre vero in un’applicazione più complessa.

Non aggiungiamo questa guardia a MissionControlComponent perché, in quanto genitore, controlla la durata del MissionService.

Spero che questo ti aiuta.

Dal momento che la soluzione di seangwright (Modifica 3) sembra essere molto utile, ho anche trovato un problema impacchettare questa funzione nel componente di base e suggerire ad altri membri del team di progetto di ricordare di chiamare super () su ngOnDestroy per triggersre questa funzione.

Questa risposta fornisce un modo per liberarsi dalla super chiamata e rendere “componenteDestroyed $” un nucleo del componente base.

 class BaseClass { protected componentDestroyed$: Subject = new Subject(); constructor() { /// wrap the ngOnDestroy to be an Observable. and set free from calling super() on ngOnDestroy. let _$ = this.ngOnDestroy; this.ngOnDestroy = () => { this.componentDestroyed$.next(); this.componentDestroyed$.complete(); _$(); } } /// placeholder of ngOnDestroy. no need to do super() call of extended class. ngOnDestroy() {} } 

E quindi puoi usare questa funzione liberamente, ad esempio:

 @Component({ selector: 'my-thing', templateUrl: './my-thing.component.html' }) export class MyThingComponent extends BaseClass implements OnInit, OnDestroy { constructor( private myThingService: MyThingService, ) { super(); } ngOnInit() { this.myThingService.getThings() .takeUntil(this.componentDestroyed$) .subscribe(things => console.log(things)); } /// optional. not a requirement to implement OnDestroy ngOnDestroy() { console.log('everything works as intended with or without super call'); } } 

Basato su: utilizzo dell’ereditarietà della class per eseguire il collegamento al ciclo di vita di 2 componenti angolari

Un altro approccio generico:

 export abstract class UnsubscribeOnDestroy implements OnDestroy { protected d$: Subject; constructor() { this.d$ = new Subject(); const f = this.ngOnDestroy; this.ngOnDestroy = () => { f(); this.d$.next(); this.d$.complete(); }; } public ngOnDestroy() { // no-op } } 

La risposta di Edit # 3 (e le varianti) ufficiali funziona bene, ma la cosa che mi dà è la ‘fangosa’ della logica di business attorno all’abbonamento osservabile.

Ecco un altro approccio usando i wrapper.

Warining: codice sperimentale

File subscribeAndGuard.ts è usato per creare una nuova estensione Observable per avvolgere .subscribe() e al suo interno per avvolgere ngOnDestroy() .
L’utilizzo è uguale a .subscribe() , ad eccezione di un primo parametro aggiuntivo che fa riferimento al componente.

 import { Observable } from 'rxjs/Observable'; import { Subscription } from 'rxjs/Subscription'; const subscribeAndGuard = function(component, fnData, fnError = null, fnComplete = null) { // Define the subscription const sub: Subscription = this.subscribe(fnData, fnError, fnComplete); // Wrap component's onDestroy if (!component.ngOnDestroy) { throw new Error('To use subscribeAndGuard, the component must implement ngOnDestroy'); } const saved_OnDestroy = component.ngOnDestroy; component.ngOnDestroy = () => { console.log('subscribeAndGuard.onDestroy'); sub.unsubscribe(); // Note: need to put original back in place // otherwise 'this' is undefined in component.ngOnDestroy component.ngOnDestroy = saved_OnDestroy; component.ngOnDestroy(); }; return sub; }; // Create an Observable extension Observable.prototype.subscribeAndGuard = subscribeAndGuard; // Ref: https://www.typescriptlang.org/docs/handbook/declaration-merging.html declare module 'rxjs/Observable' { interface Observable { subscribeAndGuard: typeof subscribeAndGuard; } } 

Ecco un componente con due abbonamenti, uno con il wrapper e uno senza. L’unica avvertenza è che deve implementare OnDestroy (con il corpo vuoto se lo si desidera), altrimenti Angular non sa di chiamare la versione avvolta.

 import { Component, OnInit, OnDestroy } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/Rx'; import './subscribeAndGuard'; @Component({ selector: 'app-subscribing', template: '

Subscribing component is active

', }) export class SubscribingComponent implements OnInit, OnDestroy { ngOnInit() { // This subscription will be terminated after onDestroy Observable.interval(1000) .subscribeAndGuard(this, (data) => { console.log('Guarded:', data); }, (error) => { }, (/*completed*/) => { } ); // This subscription will continue after onDestroy Observable.interval(1000) .subscribe( (data) => { console.log('Unguarded:', data); }, (error) => { }, (/*completed*/) => { } ); } ngOnDestroy() { console.log('SubscribingComponent.OnDestroy'); } }

Un plunker demo è qui

Una nota aggiuntiva: Modifica 3 – La soluzione ‘ufficiale’, può essere semplificata usando takeWhile () invece di takeUntil () prima delle sottoscrizioni e un semplice booleano piuttosto che un altro osservabile in ngOnDestroy.

 @Component({...}) export class SubscribingComponent implements OnInit, OnDestroy { iAmAlive = true; ngOnInit() { Observable.interval(1000) .takeWhile(() => { return this.iAmAlive; }) .subscribe((data) => { console.log(data); }); } ngOnDestroy() { this.iAmAlive = false; } } 

Mi piacciono le ultime due risposte, ma ho riscontrato un problema se la sottoclass ha fatto riferimento a "this" in ngOnDestroy .

L’ho modificato per essere questo, e sembra che abbia risolto il problema.

 export abstract class BaseComponent implements OnDestroy { protected componentDestroyed$: Subject; constructor() { this.componentDestroyed$ = new Subject(); let f = this.ngOnDestroy; this.ngOnDestroy = function() { // without this I was getting an error if the subclass had // this.blah() in ngOnDestroy f.bind(this)(); this.componentDestroyed$.next(true); this.componentDestroyed$.complete(); }; } /// placeholder of ngOnDestroy. no need to do super() call of extended class. ngOnDestroy() {} } 

I tried seangwright’s solution (Edit 3)

That is not working for Observable that created by timer or interval.

However, i got it working by using another approach:

 import { Component, OnDestroy, OnInit } from '@angular/core'; import 'rxjs/add/operator/takeUntil'; import { Subject } from 'rxjs/Subject'; import { Subscription } from 'rxjs/Subscription'; import 'rxjs/Rx'; import { MyThingService } from '../my-thing.service'; @Component({ selector: 'my-thing', templateUrl: './my-thing.component.html' }) export class MyThingComponent implements OnDestroy, OnInit { private subscriptions: Array = []; constructor( private myThingService: MyThingService, ) { } ngOnInit() { const newSubs = this.myThingService.getThings() .subscribe(things => console.log(things)); this.subscriptions.push(newSubs); } ngOnDestroy() { for (const subs of this.subscriptions) { subs.unsubscribe(); } } } 

You usually need to unsubscribe when the components get destroyed, but Angular is going to handle it more and more as we go, for example in new minor version of Angular4, they have this section for routing unsubscribe:

Do you need to unsubscribe?

As described in the ActivatedRoute: the one-stop-shop for route information section of the Routing & Navigation page, the Router manages the observables it provides and localizes the subscriptions. The subscriptions are cleaned up when the component is destroyed, protecting against memory leaks, so you don’t need to unsubscribe from the route paramMap Observable.

Also the example below is a good example from Angular to create a component and destroy it after, look at how component implements OnDestroy, if you need onInit, you also can implements it in your component, like implements OnInit, OnDestroy

 import { Component, Input, OnDestroy } from '@angular/core'; import { MissionService } from './mission.service'; import { Subscription } from 'rxjs/Subscription'; @Component({ selector: 'my-astronaut', template: ` 

{{astronaut}}: {{mission}}

` }) export class AstronautComponent implements OnDestroy { @Input() astronaut: string; mission = ''; confirmed = false; announced = false; subscription: Subscription; constructor(private missionService: MissionService) { this.subscription = missionService.missionAnnounced$.subscribe( mission => { this.mission = mission; this.announced = true; this.confirmed = false; }); } confirm() { this.confirmed = true; this.missionService.confirmMission(this.astronaut); } ngOnDestroy() { // prevent memory leak when component destroyed this.subscription.unsubscribe(); } }

Following the answer by @seangwright , I’ve written an abstract class that handles “infinite” observables’ subscriptions in components:

 import { OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs/Subscription'; import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import { PartialObserver } from 'rxjs/Observer'; export abstract class InfiniteSubscriberComponent implements OnDestroy { private onDestroySource: Subject = new Subject(); constructor() {} subscribe(observable: Observable): Subscription; subscribe( observable: Observable, observer: PartialObserver ): Subscription; subscribe( observable: Observable, next?: (value: any) => void, error?: (error: any) => void, complete?: () => void ): Subscription; subscribe(observable: Observable, ...subscribeArgs): Subscription { return observable .takeUntil(this.onDestroySource) .subscribe(...subscribeArgs); } ngOnDestroy() { this.onDestroySource.next(); this.onDestroySource.complete(); } } 

To use it, just extend it in your angular component and call the subscribe() method as follows:

 this.subscribe(someObservable, data => doSomething()); 

It also accepts the error and complete callbacks as usual, an observer object, or not callbacks at all. Remember to call super.ngOnDestroy() if you are also implementing that method in the child component.

Find here an additional reference by Ben Lesh: RxJS: Don’t Unsubscribe .

Another short addition to the above mentioned situations is:

  • Always unsubscribe, when new values in the subscribed stream is no more required or don’t matter, it will result in way less number of triggers and increase in performance in a few cases. Cases such as components where the subscribed data/event no more exists or a new subscription to an all new stream is required (refresh, etc.) is a good example for unsubscription.