--- Edycja 4 - Dodatkowe zasoby (2018/09/01)
W ostatnim odcinku Adventures in Angular Ben Lesh i Ward Bell dyskutują na temat tego, jak / kiedy wypisać się z komponentu. Dyskusja rozpoczyna się około 1:05:30.
Totem wspomina, right now there's an awful takeUntil dance that takes a lot of machinery
a Shai Reznik wspomina Angular handles some of the subscriptions like http and routing
.
W odpowiedzi Ben wspomina, że obecnie trwają dyskusje, aby umożliwić obserwatorom przyłączenie się do zdarzeń cyklu życia komponentu kątowego, a Ward sugeruje obserwowalność zdarzeń cyklu życia, do których komponent może się zapisać, aby dowiedzieć się, kiedy ukończyć obserwowalne utrzymywane jako stan wewnętrzny komponentu.
To powiedziawszy, teraz potrzebujemy rozwiązań, więc oto inne zasoby.
Rekomendacja takeUntil()
wzoru autorstwa członka zespołu RxJ, Nicholasa Jamiesona, oraz reguła tslint, aby pomóc w egzekwowaniu go. https://ncjamieson.com/avoiding-takeuntil-leaks/
Lekki pakiet npm, który ujawnia obserwowalny operator, który przyjmuje instancję składnika ( this
) jako parametr i automatycznie wypisuje się podczas ngOnDestroy
.
https://github.com/NetanelBasal/ngx-take-until-destroy
Kolejna odmiana powyższego z nieco lepszą ergonomią, jeśli nie robisz kompilacji AOT (ale wszyscy powinniśmy teraz robić AOT).
https://github.com/smnbbrv/ngx-rx-collector
Niestandardowa dyrektywa, *ngSubscribe
która działa jak potok asynchroniczny, ale tworzy widok osadzony w szablonie, dzięki czemu można odwoływać się do wartości „nieopakowanej” w całym szablonie.
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
Wspominam w komentarzu na blogu Nicholasa, że nadużywanie takeUntil()
może być oznaką, że twój komponent próbuje zrobić zbyt wiele i że należy rozważyć rozdzielenie istniejących komponentów na komponenty Feature i Presentation . Następnie można | async
obserwować z komponentu Feature do Input
komponentu Presentation, co oznacza, że nigdzie nie są potrzebne subskrypcje. Przeczytaj więcej o tym podejściu tutaj
--- Edytuj 3 - „Oficjalne” rozwiązanie (2017/04/09)
Rozmawiałem z Wardem Bellem na temat tego pytania w NGConf (nawet pokazałem mu tę odpowiedź, która, jak powiedział, była poprawna), ale powiedział mi, że zespół doktorów Angular ma rozwiązanie tego pytania, które nie zostało opublikowane (chociaż pracują nad tym, aby je zatwierdzić) ). Powiedział mi również, że mogę zaktualizować moją odpowiedź SO wraz z nadchodzącą oficjalną rekomendacją.
Rozwiązaniem, którego powinniśmy wszyscy użyć w przyszłości, jest dodanie private ngUnsubscribe = new Subject();
pola do wszystkich komponentów, które mają .subscribe()
wywołania Observable
s w swoim kodzie klasy.
Następnie wywołujemy this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();
nasze ngOnDestroy()
metody.
Tajny sos (jak już zauważył @metamaker ) ma wywoływać takeUntil(this.ngUnsubscribe)
przed każdym z naszych .subscribe()
połączeń, co zagwarantuje, że wszystkie subskrypcje zostaną wyczyszczone po zniszczeniu komponentu.
Przykład:
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();
}
}
Uwaga: Ważne jest, aby dodać takeUntil
operatora jako ostatni, aby zapobiec wyciekom z pośrednimi obserwowalnymi w łańcuchu operatora.
--- Edytuj 2 (2016/12/28)
Źródło 5
W samouczku Angular, rozdział Routing stwierdza teraz: „Router zarządza obserwowanymi przez siebie obiektami i lokalizuje subskrypcje. Subskrypcje są czyszczone po zniszczeniu komponentu, chroniąc przed wyciekiem pamięci, więc nie musimy rezygnować z subskrypcji trasa jest możliwa do zaobserwowania. ” - Mark Rajcok
Oto dyskusja na temat Github dla dokumentów Angular dotyczących Router Observables, w której Ward Bell wspomina, że wyjaśnienie tego wszystkiego znajduje się w pracach.
--- Edytuj 1
Źródło 4
W tym filmie NgEurope Rob Wormald mówi również, że nie musisz rezygnować z subskrypcji Router Observables. Wspomina także o http
usłudze oraz ActivatedRoute.params
w tym filmie z listopada 2016 r .
--- Oryginalna odpowiedź
TLDR:
Dla tego pytania istnieją (2) rodzaje Observables
- wartość skończona i wartość nieskończona .
http
Observables
tworzy wartości skończone (1) i coś w rodzaju DOM event listener
Observables
tworzy wartości nieskończone .
Jeśli ręcznie wywołujesz subscribe
(nie używając potoku asynchronicznego), to unsubscribe
z nieskończoności Observables
.
Nie przejmuj się skończonymi , RxJs
zajmę się nimi.
Źródło 1
Tutaj odnalazłem odpowiedź Roba Wormalda w Angular's Gitter .
Mówi (zreorganizowałem dla jasności i podkreślenia jest moje)
jeśli jest to sekwencja o pojedynczej wartości (jak żądanie HTTP), ręczne czyszczenie nie jest konieczne (zakładając, że subskrybujesz ręcznie kontroler)
powinienem powiedzieć „jeśli jest to sekwencja, która się kończy ” (z których jedna sekwencja wartości, a la http, jest jedną)
jeśli jest to nieskończona sekwencja , należy zrezygnować z subskrypcji, którą wykonuje dla ciebie potok asynchroniczny
Wspomina również w tym filmie na YouTubie o Observables, który they clean up after themselves
... w kontekście Observables, które complete
(jak Obietnice, które zawsze się kończą, ponieważ zawsze generują 1 wartość i kończą się - nigdy nie martwiliśmy się o rezygnację z subskrypcji Obietnic, aby upewnić się, że posprzątają xhr
wydarzenie słuchacze, prawda?).
Źródło 2
Również w przewodniku Rangle do Angulara 2 czytamy
W większości przypadków nie będziemy musieli jawnie wywoływać metody anulowania subskrypcji, chyba że chcemy wcześniej anulować subskrypcję lub nasz Observable ma dłuższą żywotność niż nasza subskrypcja. Domyślnym zachowaniem obserwowalnych operatorów jest usunięcie subskrypcji, gdy tylko zostaną opublikowane komunikaty .complete () lub .error (). Należy pamiętać, że RxJS został zaprojektowany do używania przez większość czasu w stylu „ognia i zapomnienia”.
Kiedy to wyrażenie ma our Observable has a longer lifespan than our subscription
zastosowanie?
Ma to zastosowanie, gdy subskrypcja jest tworzona wewnątrz komponentu, który jest niszczony przed (lub „długo” przed) Observable
zakończeniem.
Czytam to jako znaczące, że jeśli zasubskrybujemy http
żądanie lub obserwowalny, który emituje 10 wartości, a nasz komponent zostanie zniszczony przed http
zwróceniem tego żądania lub wysłaniem 10 wartości, nadal mamy się dobrze!
Gdy żądanie zostanie zwrócone lub dziesiąta wartość zostanie ostatecznie wysłana, Observable
zostanie zakończone, a wszystkie zasoby zostaną oczyszczone.
Źródło 3
Jeśli spojrzymy na ten przykład z tego samego rangle kierować widzimy, że Subscription
aby route.params
nie wymagać unsubscribe()
, bo nie wiemy, kiedy te params
przestaną zmianę (emitując nowe wartości).
Komponent może zostać zniszczony przez odejście, w którym to przypadku parametry trasy prawdopodobnie nadal będą się zmieniać (mogą technicznie zmieniać się aż do końca aplikacji), a zasoby przydzielone w subskrypcji będą nadal przydzielone, ponieważ nie było completion
.
Subscription
s, abyhttp-requests
można zignorować, jak nazywają tylkoonNext
raz, a potem nazywająonComplete
.Router
Zamiast nazywaonNext
wielokrotnie i nigdy nie może zadzwonićonComplete
(nie wiem o tym ...). To samo dotyczyObservable
s odEvent
s. Sądzę, że tak powinno byćunsubscribed
.