--- Edición 4 - Recursos adicionales (2018/09/01)
En un episodio reciente de Adventures in Angular, Ben Lesh y Ward Bell discuten los problemas en torno a cómo / cuándo darse de baja en un componente. La discusión comienza aproximadamente a las 1:05:30.
Ward menciona right now there's an awful takeUntil dance that takes a lot of machinery
y Shai Reznik menciona Angular handles some of the subscriptions like http and routing
.
En respuesta, Ben menciona que hay discusiones en este momento para permitir que los Observables se conecten a los eventos del ciclo de vida del componente Angular y Ward sugiere un Observable de los eventos del ciclo de vida al que un componente podría suscribirse para saber cuándo completar los Observables mantenidos como estado interno del componente.
Dicho esto, en su mayoría necesitamos soluciones ahora, así que aquí hay algunos otros recursos.
Una recomendación para el takeUntil()
patrón del miembro del equipo central de RxJ, Nicholas Jamieson, y una regla tslint para ayudar a hacerla cumplir. https://ncjamieson.com/avoiding-takeuntil-leaks/
Paquete npm ligero que expone un operador Observable que toma una instancia de componente ( this
) como parámetro y se da de baja automáticamente durante ngOnDestroy
.
https://github.com/NetanelBasal/ngx-take-until-destroy
Otra variación de lo anterior con una ergonomía ligeramente mejor si no está haciendo AOT builds (pero todos deberíamos estar haciendo AOT ahora).
https://github.com/smnbbrv/ngx-rx-collector
Directiva personalizada *ngSubscribe
que funciona como una tubería asíncrona pero crea una vista incrustada en su plantilla para que pueda consultar el valor 'sin envolver' en toda su plantilla.
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
Menciono en un comentario al blog de Nicholas que el uso excesivo de takeUntil()
podría ser una señal de que su componente está tratando de hacer demasiado y que se debe considerar la separación de sus componentes existentes en componentes de Presentación y Característica . Luego puede hacer el Observable desde el componente Característica en uno del componente Presentacional, lo que significa que no se necesitan suscripciones en ningún lado. Lea más sobre este enfoque aquí| async
Input
--- Edición 3 - La solución 'oficial' (2017/04/09)
Hablé con Ward Bell sobre esta pregunta en NGConf (incluso le mostré esta respuesta y dijo que era correcta), pero me dijo que el equipo de documentación de Angular tenía una solución para esta pregunta que no se ha publicado (aunque están trabajando para lograr que sea aprobada). ) También me dijo que podía actualizar mi respuesta SO con la próxima recomendación oficial.
La solución que todos deberíamos usar en el futuro es agregar un private ngUnsubscribe = new Subject();
campo a todos los componentes que tienen .subscribe()
llamadas a Observable
s dentro de su código de clase.
Luego llamamos this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();
a nuestros ngOnDestroy()
métodos.
La salsa secreta (como ya señaló @metamaker ) es llamar takeUntil(this.ngUnsubscribe)
antes de cada una de nuestras .subscribe()
llamadas, lo que garantizará que todas las suscripciones se limpien cuando se destruya el componente.
Ejemplo:
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: Es importante agregar el takeUntil
operador como el último para evitar fugas con observables intermedios en la cadena del operador.
--- Edición 2 (28/12/2016)
Fuente 5
El tutorial Angular, el capítulo Enrutamiento ahora establece lo siguiente: "El enrutador administra los observables que proporciona y localiza las suscripciones. Las suscripciones se limpian cuando se destruye el componente, protegiéndolo contra pérdidas de memoria, por lo que no necesitamos cancelar la suscripción de los parámetros de ruta observables ". - Mark Rajcok
Aquí hay una discusión sobre los problemas de Github para los documentos angulares con respecto a los observadores del enrutador, donde Ward Bell menciona que se está aclarando todo esto.
--- Editar 1
Fuente 4
En este video de NgEurope, Rob Wormald también dice que no es necesario darse de baja de Router Observables. También menciona el http
servicio y ActivatedRoute.params
en este video de noviembre de 2016 .
--- Respuesta original
TLDR:
Para esta pregunta hay (2) tipos de Observables
- valor finito y valor infinito .
http
Observables
producir valores finitos (1) y algo así como un DOM event listener
Observables
produce valores infinitos .
Si llama manualmente subscribe
(sin usar tubería asíncrona), entonces unsubscribe
desde infinito Observables
.
No te preocupes por los finitos , los cuidaremosRxJs
.
Fuente 1
Rastreé una respuesta de Rob Wormald en Angular's Gitter aquí .
Él afirma (me reorganicé para mayor claridad y el énfasis es mío)
si es una secuencia de un solo valor (como una solicitud http), la limpieza manual es innecesaria (suponiendo que se suscriba manualmente en el controlador)
debería decir "si es una secuencia que se completa " (de las cuales las secuencias de valor único, a la http, son una)
si es una secuencia infinita , debe darse de baja, lo que la tubería asíncrona hace por usted
También menciona en este video de YouTube en Observables que they clean up after themselves
... en el contexto de Observables que complete
(como Promesas, que siempre se completan porque siempre producen 1 valor y finalizan), nunca nos preocupamos por cancelar la suscripción a Promesas para asegurarnos de que limpien el xhr
evento oyentes, ¿verdad?
Fuente 2
También en la guía Rangle de Angular 2 se lee
En la mayoría de los casos, no necesitaremos llamar explícitamente al método de cancelación de la suscripción a menos que queramos cancelar antes o que nuestro Observable tenga una vida útil más larga que nuestra suscripción. El comportamiento predeterminado de los operadores Observables es deshacerse de la suscripción tan pronto como se publiquen los mensajes .complete () o .error (). Tenga en cuenta que RxJS fue diseñado para ser utilizado en una forma de "disparar y olvidar" la mayor parte del tiempo.
¿Cuándo se aplica la frase our Observable has a longer lifespan than our subscription
?
Se aplica cuando se crea una suscripción dentro de un componente que se destruye antes (o no 'mucho' antes) de que se Observable
complete.
Leí esto como significado si nos suscribimos a una http
solicitud o un observable que emite 10 valores y nuestro componente se destruye antes de que esa http
solicitud regrese o se hayan emitido los 10 valores, ¡todavía estamos bien!
Cuando la solicitud regrese o finalmente se emita el décimo valor, Observable
se completará y todos los recursos se limpiarán.
Fuente 3
Si miramos este ejemplo de la misma guía Rangle, podemos ver que el Subscription
to route.params
requiere un unsubscribe()
porque no sabemos cuándo params
dejarán de cambiar (emitiendo nuevos valores).
El componente podría destruirse navegando, en cuyo caso los parámetros de ruta probablemente seguirán cambiando (técnicamente podrían cambiar hasta que finalice la aplicación) y los recursos asignados en la suscripción aún se asignarían porque no ha habido un completion
.
Subscription
quehttp-requests
se puede ignorar, ya que solo llamanonNext
una vez y luego llamanonComplete
. En suRouter
lugar, llamaonNext
repetidamente y puede que nunca llameonComplete
(no estoy seguro de eso ...). Lo mismo ocurre conObservable
s deEvent
s. Así que supongo que deberían serunsubscribed
.