--- Editar 4 - Recursos adicionais (01/09/2018)
Em um episódio recente de Adventures in Angular, Ben Lesh e Ward Bell discutem as questões sobre como / quando cancelar a inscrição em um componente. A discussão começa por volta das 1:05:30.
Ward menciona right now there's an awful takeUntil dance that takes a lot of machinery
e Shai Reznik menciona Angular handles some of the subscriptions like http and routing
.
Em resposta, Ben menciona que há discussões no momento para permitir que os Observables se conectem aos eventos do ciclo de vida do componente Angular e Ward sugere um Observável dos eventos do ciclo de vida que um componente pode assinar como uma maneira de saber quando concluir os Observables mantidos como estado interno do componente.
Dito isto, precisamos principalmente de soluções agora, então aqui estão alguns outros recursos.
Uma recomendação para o takeUntil()
padrão do membro da equipe principal do RxJs, Nicholas Jamieson, e uma regra tslint para ajudar a aplicá-lo. https://ncjamieson.com/avoiding-takeuntil-leaks/
Pacote npm leve que expõe um operador Observable que usa uma instância de componente (this
) como parâmetro e cancela automaticamente a inscrição durante ngOnDestroy
.
https://github.com/NetanelBasal/ngx-take-until-destroy
Outra variação do exposto acima com ergonomia um pouco melhor se você não estiver executando o AOT (mas todos nós deveríamos estar executando o AOT agora).
https://github.com/smnbbrv/ngx-rx-collector
Diretiva personalizada *ngSubscribe
que funciona como canal assíncrono, mas cria uma exibição incorporada no seu modelo para que você possa consultar o valor 'desembrulhado' em todo o modelo.
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
Menciono em um comentário no blog de Nicholas que o uso excessivo de takeUntil()
pode ser um sinal de que seu componente está tentando fazer muito e que a separação dos componentes existentes em componentes de recursos e de apresentação deve ser considerada. Você pode então| async
observar o componente Observável do componente Input
Apresentacional, o que significa que nenhuma assinatura é necessária em nenhum lugar. Leia mais sobre essa abordagem aqui
--- Editar 3 - A solução 'oficial' (09/04/2017)
Conversei com Ward Bell sobre esta questão na NGConf (eu até lhe mostrei a resposta que ele dizia estar correta), mas ele me disse que a equipe de documentos da Angular tinha uma solução para essa pergunta que não é publicada (embora eles estejam trabalhando para aprová-la) ) Ele também me disse que eu poderia atualizar minha resposta do SO com a próxima recomendação oficial.
A solução que todos devemos usar daqui para frente é adicionar um private ngUnsubscribe = new Subject();
campo a todos os componentes que possuem .subscribe()
chamadas para Observable
s dentro do código de classe.
Então chamamos this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();
nossos ngOnDestroy()
métodos.
O segredo (como já observado pelo @metamaker ) é ligar takeUntil(this.ngUnsubscribe)
antes de cada uma das nossas .subscribe()
chamadas, o que garantirá que todas as assinaturas serão limpas quando o componente for destruído.
Exemplo:
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 adicionar otakeUntil
operador como o último para evitar vazamentos com observáveis intermediários na cadeia do operador.
--- Editar 2 (28/12/2016)
Fonte 5
O tutorial Angular, o capítulo Roteamento, agora declara o seguinte: "O roteador gerencia os observáveis que fornece e localiza as assinaturas. As assinaturas são limpas quando o componente é destruído, protegendo contra vazamentos de memória, para que não seja necessário cancelar a assinatura de a rota params Observable. " - Mark Rajcok
Aqui está uma discussão sobre as questões do Github para os documentos angulares sobre roteadores observáveis, onde Ward Bell menciona que há esclarecimentos para tudo isso.
--- Editar 1
Fonte 4
Neste vídeo do NgEurope, Rob Wormald também diz que não é necessário cancelar a assinatura do Router Observables. Ele também menciona o http
serviço e ActivatedRoute.params
, neste vídeo, de novembro de 2016 .
--- Resposta original
TLDR:
Para esta pergunta, existem (2) tipos de Observables
- valor finito e valor infinito .
http
Observables
produza valores finitos (1) e algo como um DOM event listener
Observables
produz valores infinitos .
Se você chamar manualmente subscribe
(sem usar tubulação assíncrona), em seguida, unsubscribe
a partir infinito Observables
.
Não se preocupe com os finitos , RxJs
cuidará deles.
Fonte 1
Encontrei uma resposta de Rob Wormald em Gitter da Angular aqui .
Ele afirma (eu reorganizei para maior clareza e ênfase é minha)
se for uma sequência de valor único (como uma solicitação http), a limpeza manual é desnecessária (supondo que você assine o controlador manualmente)
eu deveria dizer "se é uma sequência que termina " (das quais seqüências de valor único, a la http, são uma)
se for uma sequência infinita , cancele a inscrição, o que o canal assíncrono faz por você
Ele também menciona neste vídeo do YouTube sobre Observables que they clean up after themselves
... no contexto dos Observables que complete
(como Promises, que sempre são concluídas porque sempre produzem 1 valor e terminam - nunca nos preocupamos em cancelar a inscrição da Promises para garantir que eles limpem o xhr
evento ouvintes, certo?).
Fonte 2
Também no guia Rangle para Angular 2, ele lê
Na maioria dos casos, não precisaremos chamar explicitamente o método de cancelamento de inscrição, a menos que desejemos cancelar mais cedo ou nosso Observable tenha uma vida útil mais longa do que nossa assinatura. O comportamento padrão dos operadores Observáveis é descartar a assinatura assim que as mensagens .complete () ou .error () forem publicadas. Lembre-se de que o RxJS foi projetado para ser usado da maneira "atire e esqueça" na maioria das vezes.
Quando a frase se our Observable has a longer lifespan than our subscription
aplica?
Aplica-se quando uma assinatura é criada dentro de um componente que é destruído antes (ou não 'muito' antes) da Observable
conclusão.
Eu li isso como significando que se subscrevermos uma http
solicitação ou um observável que emita 10 valores e nosso componente for destruído antes que a http
solicitação retorne ou que os 10 valores tenham sido emitidos, ainda estamos ok!
Quando a solicitação retornar ou o décimo valor finalmente for emitido, a Observable
conclusão será concluída e todos os recursos serão limpos.
Fonte 3
Se olharmos para este exemplo a partir do mesmo guia Rangle, podemos ver que o Subscription
to route.params
requer um unsubscribe()
porque não sabemos quando eles params
irão parar de mudar (emitindo novos valores).
O componente pode ser destruído navegando para longe; nesse caso, os parâmetros de rota provavelmente ainda estarão mudando (eles podem tecnicamente mudar até o aplicativo terminar) e os recursos alocados na assinatura ainda serão alocados porque não houve a completion
.
Subscription
quehttp-requests
pode ser ignorado, pois eles ligam apenasonNext
uma vez e depois ligamonComplete
. EmRouter
vez disso, chamaonNext
repetidamente e pode nunca ligaronComplete
(não tem certeza disso ...). O mesmo vale paraObservable
s deEvent
s. Então acho que deveriam serunsubscribed
.