--- Modifica 4 - Risorse aggiuntive (2018/09/01)
In un recente episodio di Adventures in Angular, Ben Lesh e Ward Bell discutono dei problemi su come / quando annullare l'iscrizione a un componente. La discussione inizia alle 1:05:30 circa.
Menzioni di rione right now there's an awful takeUntil dance that takes a lot of machinerye menzioni di Shai Reznik Angular handles some of the subscriptions like http and routing.
In risposta Ben menziona che in questo momento ci sono discussioni per consentire a Observables di agganciarsi agli eventi del ciclo di vita dei componenti angolari e Ward suggerisce un osservabile di eventi del ciclo di vita a cui un componente potrebbe iscriversi come un modo per sapere quando completare gli osservabili mantenuti come stato interno del componente.
Detto questo, ora abbiamo principalmente bisogno di soluzioni, quindi ecco alcune altre risorse.
Una raccomandazione per lo takeUntil()schema da parte del membro del team principale di RxJ Nicholas Jamieson e una regola tslint per aiutare a farla rispettare. https://ncjamieson.com/avoiding-takeuntil-leaks/
Pacchetto npm leggero che espone un operatore Observable che accetta un'istanza del componente ( this) come parametro e annulla automaticamente l'iscrizione durante ngOnDestroy.
https://github.com/NetanelBasal/ngx-take-until-destroy
Un'altra variante di quanto sopra con un'ergonomia leggermente migliore se non stai eseguendo build AOT (ma dovremmo tutti fare AOT ora).
https://github.com/smnbbrv/ngx-rx-collector
Direttiva personalizzata *ngSubscribeche funziona come una pipe asincrona ma crea una vista incorporata nel modello in modo da poter fare riferimento al valore "non imballato" in tutto il modello.
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
Cito in un commento al blog di Nicholas che un uso eccessivo di takeUntil()potrebbe essere un segno che il componente sta provando a fare troppo e che occorre considerare la separazione dei componenti esistenti in componenti Feature e Presentational . È quindi possibile | asyncosservare l'Osservabile dal componente Feature in uno Inputdel componente Presentational, il che significa che non sono necessari abbonamenti 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 anche mostrato questa risposta che ha detto che era corretta) ma mi ha detto che il team di docenti di Angular aveva una soluzione a questa domanda che non è stata pubblicata (anche se stanno lavorando per ottenere l'approvazione ). Mi disse anche che avrei potuto aggiornare la mia risposta SO con l'imminente raccomandazione ufficiale.
La soluzione che dovremmo usare in futuro è quella di aggiungere un private ngUnsubscribe = new Subject();campo a tutti i componenti che hanno .subscribe()chiamate a Observableall'interno del loro codice di classe.
Chiamiamo quindi i this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();nostri ngOnDestroy()metodi.
La salsa segreta (come già notato da @metamaker ) è quella di chiamare takeUntil(this.ngUnsubscribe)prima di ciascuna delle nostre .subscribe()chiamate che garantirà che tutti gli abbonamenti vengano ripuliti 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' takeUntiloperatore come ultimo per evitare perdite con osservabili intermedi nella catena dell'operatore.
--- Modifica 2 (28/12/2016)
Fonte 5
Il tutorial di Angular, il capitolo Routing ora afferma quanto segue: "Il router gestisce gli osservabili che fornisce e localizza gli abbonamenti. Gli abbonamenti vengono ripuliti quando il componente viene distrutto, proteggendo da perdite di memoria, quindi non è necessario annullare l'iscrizione a il percorso è osservabile. " - Mark Rajcok
Ecco una discussione sulle questioni di Github per i documenti angolari riguardanti gli osservabili del router in cui Ward Bell menziona che è in corso un chiarimento per tutto ciò.
--- Modifica 1
Fonte 4
In questo video di NgEurope Rob Wormald afferma anche che non è necessario annullare l'iscrizione a Router Observables. Cita anche il httpservizio e ActivatedRoute.paramsin questo video di novembre 2016 .
--- Risposta originale
TLDR:
Per questa domanda ci sono (2) tipi di Observables- valore finito e valore infinito .
http Observablesproduce valori finiti (1) e qualcosa come un DOM event listener Observablesproduce valori infiniti .
Se chiamate manualmente subscribe(non usando la pipa asincrona), quindi unsubscribeda infinito Observables .
Non preoccuparti di quelli finiti , RxJssi prenderanno cura di loro.
Fonte 1
Ho rintracciato una risposta di Rob Wormald in Angular's Gitter qui .
Egli afferma (ho riorganizzato per chiarezza e l'enfasi è mia)
se si tratta di una sequenza a valore singolo (come una richiesta http) la pulizia manuale non è necessaria (supponendo che abbiate sottoscritto manualmente il controller)
dovrei dire "se è una sequenza che completa " (di cui le sequenze a valore singolo, alla http, sono una)
se è una sequenza infinita , dovresti annullare l' iscrizione della pipe asincrona che fa per te
Inoltre menziona in questo video di YouTube su Observables che they clean up after themselves... nel contesto di Observables che complete(come Promises, che completano sempre perché producono sempre 1 valore e terminano - non ci siamo mai preoccupati di annullare l'iscrizione a Promises per assicurarci che ripulissero l' xhrevento ascoltatori, vero?).
Fonte 2
Anche nella guida Rangle a Angular 2 si legge
Nella maggior parte dei casi non avremo bisogno di chiamare esplicitamente il metodo di annullamento dell'iscrizione a meno che non desideriamo annullare anticipatamente o se il nostro osservabile ha una durata maggiore rispetto al nostro abbonamento. Il comportamento predefinito degli operatori osservabili è di disporre dell'abbonamento non appena vengono pubblicati i messaggi .complete () o .error (). Tieni presente che RxJS è stato progettato per essere utilizzato in modo "incendi e dimentica" per la maggior parte del tempo.
Quando si applica la frase our Observable has a longer lifespan than our subscription?
Si applica quando viene creato un abbonamento all'interno di un componente che viene distrutto prima (o non "molto prima") del Observablecompletamento.
Ho letto questo come significato se ci iscriviamo a una httprichiesta o a un osservabile che emette 10 valori e il nostro componente viene distrutto prima che la httprichiesta ritorni o che i 10 valori siano stati emessi, siamo ancora ok!
Quando la richiesta viene restituita o viene finalmente emesso il decimo valore, il processo Observableverrà completato e tutte le risorse verranno ripulite.
Fonte 3
Se guardiamo questo esempio dalla stessa guida di Rangle possiamo vedere che il Subscriptionto route.paramsrichiede un unsubscribe()perché non sappiamo quando paramssmetteranno di cambiare (emettendo nuovi valori).
Il componente potrebbe essere distrutto allontanandosi nel qual caso i parametri del percorso probabilmente continueranno a cambiare (potrebbero cambiare tecnicamente fino al termine dell'app) e le risorse allocate nell'abbonamento verrebbero comunque allocate perché non è presente un completion.
Subscriptionche sihttp-requestspossa ignorare, poiché chiamano soloonNextuna volta e poi chiamanoonComplete. IlRouterinvece chiamaonNextripetutamente e potrebbe mai chiamareonComplete(non sicuro di questo ...). Lo stesso vale perObservables daEvents. Quindi immagino che dovrebbero essereunsubscribed.