--- Bearbeiten 4 - Zusätzliche Ressourcen (2018/09/01)
In einer kürzlich erschienenen Folge von Adventures in Angular diskutieren Ben Lesh und Ward Bell die Fragen, wie / wann eine Komponente abgemeldet werden muss. Die Diskussion beginnt gegen 1:05:30 Uhr.
Ward erwähnt right now there's an awful takeUntil dance that takes a lot of machinery
und Shai Reznik erwähnt Angular handles some of the subscriptions like http and routing
.
Als Antwort darauf erwähnt Ben, dass es derzeit Diskussionen gibt, die es Observables ermöglichen, sich in die Lebenszyklusereignisse der Angular-Komponente einzubinden, und Ward schlägt eine Observable von Lebenszyklusereignissen vor, die eine Komponente abonnieren könnte, um zu wissen, wann Observables abgeschlossen werden müssen, die als interner Status der Komponente verwaltet werden.
Das heißt, wir brauchen jetzt meistens Lösungen. Hier sind einige andere Ressourcen.
Eine Empfehlung für das takeUntil()
Muster von RxJs Kernteammitglied Nicholas Jamieson und eine tslint-Regel, um es durchzusetzen. https://ncjamieson.com/avoiding-takeuntil-leaks/
Leichtes npm-Paket, das einen Observable-Operator verfügbar macht, der eine Komponenteninstanz ( this
) als Parameter verwendet und sich währenddessen automatisch abmeldet ngOnDestroy
.
https://github.com/NetanelBasal/ngx-take-until-destroy
Eine weitere Variante des oben genannten mit etwas besserer Ergonomie, wenn Sie keine AOT-Builds ausführen (aber wir sollten jetzt alle AOT ausführen).
https://github.com/smnbbrv/ngx-rx-collector
Benutzerdefinierte Direktive *ngSubscribe
, die wie eine asynchrone Pipe funktioniert, jedoch eine eingebettete Ansicht in Ihrer Vorlage erstellt, sodass Sie in Ihrer Vorlage auf den Wert "Unwrapped" verweisen können.
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
In einem Kommentar zu Nicholas 'Blog erwähne ich, dass eine Überbeanspruchung takeUntil()
ein Zeichen dafür sein könnte, dass Ihre Komponente versucht, zu viel zu tun, und dass die Trennung Ihrer vorhandenen Komponenten in Feature- und Präsentationskomponenten in Betracht gezogen werden sollte. Sie können dann | async
das Observable von der Feature-Komponente in eine Input
der Presentational-Komponenten umwandeln, was bedeutet, dass nirgendwo Abonnements erforderlich sind. Lesen Sie mehr über diesen Ansatz hier
--- Edit 3 - Die 'offizielle' Lösung (2017/04/09)
Ich habe mit Ward Bell über diese Frage bei NGConf gesprochen (ich habe ihm sogar diese Antwort gezeigt, die er für richtig hielt), aber er hat mir gesagt, dass das Docs-Team für Angular eine Lösung für diese Frage hat, die nicht veröffentlicht wurde (obwohl sie daran arbeiten, sie zu genehmigen ). Er sagte mir auch, ich könnte meine SO-Antwort mit der bevorstehenden offiziellen Empfehlung aktualisieren.
Die Lösung, die wir alle in Zukunft verwenden sollten, besteht darin private ngUnsubscribe = new Subject();
, allen Komponenten ein Feld hinzuzufügen, deren Klassencode s .subscribe()
aufruft Observable
.
Wir rufen dann this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();
unsere ngOnDestroy()
Methoden auf.
Die geheime Sauce (wie bereits von @metamaker erwähnt ) besteht darin, takeUntil(this.ngUnsubscribe)
vor jedem unserer .subscribe()
Anrufe anzurufen, um sicherzustellen , dass alle Abonnements bereinigt werden, wenn die Komponente zerstört wird.
Beispiel:
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();
}
}
Hinweis: Es ist wichtig, den takeUntil
Bediener als letzten hinzuzufügen , um Undichtigkeiten mit dazwischen liegenden Observablen in der Bedienerkette zu vermeiden.
--- Bearbeiten 2 (28.12.2016)
Quelle 5
Im Angular-Lernprogramm im Kapitel "Routing" heißt es nun: "Der Router verwaltet die von ihm bereitgestellten Observables und lokalisiert die Abonnements. Die Abonnements werden bereinigt, wenn die Komponente zerstört wird, und schützen so vor Speicherlecks, sodass wir uns nicht abmelden müssen." die Routenparameter beobachtbar. " - Mark Rajcok
Hier ist eine Diskussion über die Github-Probleme in den Angular-Dokumenten zu Router Observables, in denen Ward Bell erwähnt, dass eine Klärung all dessen in Arbeit ist.
--- Bearbeiten 1
Quelle 4
In diesem Video von NgEurope sagt Rob Wormald auch, dass Sie Router Observables nicht abbestellen müssen. Er erwähnt auch den http
Service und ActivatedRoute.params
in diesem Video vom November 2016 .
--- Ursprüngliche Antwort
TLDR:
Für diese Frage gibt es (2) Arten von Observables
- endlichem Wert und unendlichem Wert.
http
Observables
erzeugen endliche (1) Werte und so etwas wie ein DOM event listener
Observables
erzeugen unendliche Werte.
Wenn Sie manuell aufrufen subscribe
(ohne asynchrone Pipe), dann unsubscribe
von unendlich Observables
.
Mach dir keine Sorgen um endliche , RxJs
werde dich um sie kümmern.
Quelle 1
Ich habe hier eine Antwort von Rob Wormald in Angulars Gitter gefunden .
Er sagt (ich habe mich aus Gründen der Klarheit neu organisiert und die Betonung liegt bei mir)
Wenn es sich um eine Einzelwertsequenz handelt (wie bei einer http-Anforderung), ist die manuelle Bereinigung nicht erforderlich (vorausgesetzt, Sie abonnieren den Controller manuell).
Ich sollte sagen "wenn es eine Sequenz ist, die abgeschlossen ist " (von denen Einzelwertsequenzen, a la http, eine sind)
Wenn es sich um eine unendliche Sequenz handelt , sollten Sie sich abmelden, was die asynchrone Pipe für Sie erledigt
Außerdem erwähnt er in diesem Youtube-Video zu Observables, dass they clean up after themselves
... im Zusammenhang mit Observables, die complete
(wie Promises, die immer vollständig sind, weil sie immer einen Wert produzieren und enden) - wir haben uns nie Sorgen gemacht, Promises abzumelden, um sicherzustellen, dass sie das xhr
Ereignis bereinigen Zuhörer, richtig?).
Quelle 2
Auch in der Rangle-Anleitung zu Angular 2 heißt es
In den meisten Fällen müssen wir die Abmeldemethode nicht explizit aufrufen, es sei denn, wir möchten vorzeitig kündigen oder unser Observable hat eine längere Lebensdauer als unser Abonnement. Das Standardverhalten von Observable-Operatoren besteht darin, das Abonnement zu entsorgen, sobald .complete () - oder .error () -Nachrichten veröffentlicht werden. Denken Sie daran, dass RxJS die meiste Zeit so konzipiert wurde, dass es "Feuer und Vergessen" verwendet.
Wann gilt der Satz our Observable has a longer lifespan than our subscription
?
Dies gilt, wenn ein Abonnement in einer Komponente erstellt wird, die vor (oder nicht lange vor) dem Observable
Abschluss zerstört wird.
Ich habe dies als Bedeutung gelesen, wenn wir eine http
Anfrage oder ein Observable abonnieren , das 10 Werte ausgibt und unsere Komponente zerstört wird, bevor diese http
Anfrage zurückkehrt oder die 10 Werte ausgegeben wurden, sind wir immer noch in Ordnung!
Wenn die Anforderung zurückkehrt oder der 10. Wert endgültig ausgegeben Observable
wird, ist der Vorgang abgeschlossen und alle Ressourcen werden bereinigt.
Quelle 3
Wenn wir uns dieses Beispiel aus derselben Rangle-Anleitung ansehen, können wir sehen, dass für das Subscription
to ein route.params
erforderlich ist, unsubscribe()
da wir nicht wissen, wann sich diese params
nicht mehr ändern (neue Werte ausgeben ).
Die Komponente könnte durch Wegnavigation zerstört werden. In diesem Fall ändern sich die Routenparameter wahrscheinlich noch (sie können sich technisch bis zum Ende der App ändern), und die im Abonnement zugewiesenen Ressourcen werden weiterhin zugewiesen, da keine vorhanden ist completion
.
Subscription
s tohttp-requests
kann ignoriert werden, da sie nuronNext
einmal anrufen und dann anrufenonComplete
. DerRouter
ruft stattdessenonNext
wiederholt an und ruft möglicherweise nie anonComplete
(nicht sicher ...). Gleiches gilt fürObservable
s vonEvent
s. Also ich denke das sollte seinunsubscribed
.