Evitando problemas com Observables.
Carlos Moreira
Posted on October 27, 2019
O Reactive Extensions Library for JavaScript (RxJS) é uma biblioteca muito poderosa, isso é inegável. Não foi à toa que a equipe do Angular a integrou em seu framework. Mas com grandes poderes vem grandes responsabilidades dentre estas estão os Observables.
Por que devo me preocupar com isso?
Fazendo uma analogia imagine que você se inscreveu em vários canais do youtube, mas alguns desses canais já não são mais do seu interesse. Enquanto você não anular a inscrição (unsubscribe
), vai continuar recebendo as notificações desses canais.
Com Observable não é diferente. Ao chamar o método subscribe()
, sua aplicação irá observar os eventos emitidos até que você diga que não tem mais interesse.
No exemplo abaixo usamos o operador interval
que define um intervalo de tempo (passado como parâmetro) no qual o Observable irá emitir um evento.
Note que também vamos imprimir no console quando o componente for destruído, usando o lifecycle hook ngOnDestroy
.
Nossa aplicação de exemplo tem duas páginas, onde é exibido o componente Alpha em uma e o Beta em outra.
Mesmo depois do componente Alpha ter sido destruído, os eventos do nosso Observable continuaram a chegar :(
Isso pode gerar comportamentos inesperados, problemas de performance, problemas de gerenciamento de memória etc.
Cuidando dos Observables
Devemos anular a inscrição de nossos Observables e podemos fazer isso das seguintes maneiras:
1. Usando o unsubscribe()
Quando nos inscrevemos em um Observable nos é retornado um objeto Subscription, que por sua vez, representa um recurso descartável. Podemos guardar a referência desse objeto para chamar o unsubscribe()
no momento oportuno, como no lifecycle hook ngOnDestroy
por exemplo.
Caso tenha mais de um Observable, podemos criar um array
e guardar a referência das subscrições e assim como no exemplo anterior, no lifecycle hook ngOnDestroy
chamar o unsubscribe()
, porém dessa vez num laço forEach
.
2. Usando takeUntil
Podemos também abusar mais do poder do RxJS e utilizar seus operadores para anular as inscrições, usando por exemplo o operador takeUntil
.
Atenção ao usar essa técnica, pois caso tenha outro operador após o takeUntil
, como por exemplo operadores de transformação ou composição, pode ocorrer um subscription leak e não funcionar como esperado. Nicholas Jamieson explica esse problema com mais detalhes.
A dica é deixar esse operador por último sempre que possível.
3. Usando o AsyncPipe
Também podemos deixar na conta do Angular, fazendo com que ele se encarregue da anulação da inscrição.
Para isso, não faremos a inscrição diretamente no Observable. Passaremos essa tarefa para o nosso template usando as diretivas *ngFor
ou *ngIf
em conjunto com o AsyncPipe
Note que não chamamos o subscribe()
dessa vez.
No template, atenção ao *ngIf="someDataAsObservable$ | async as i; else loading"
, ao usar o | async
pedimos para o Angular se inscrever nesse Observable e entregar o resultado para a variável i
assim que disponível.
Enquanto o valor não estiver disponível o if
será avaliado como false
e será exibido o conteúdo do nosso ng-template
, conforme indicado no else
.
Vamos ver como ficou:
Note que ao navegar para a página do componente Beta, paramos de ouvir os eventos do nosso Observable sem precisar chamar manualmente o unsubscribe()
.
E as chamadas http pelo HttpClient
, também devo me preocupar ?
Essas estão na conta do Angular. Podemos confirmar no código fonte, a partir da linha 208, que após a resposta do servidor é chamado o complete()
do Observable que pelo seu contrato, indica que já não vai mais emitir evento nenhum.
Então, menos uma preocupação pra gente :)
Resumindo
Essas são algumas maneiras de anular a inscrição de um Observable, ajudando a manter a aplicação saudável, sem uso desnecessário dos recursos do seu cliente e evitando problemas a medida que a aplicação cresce.
Não deixe seus Observables espalhados por aí ou eles podem te assombrar depois.
Posted on October 27, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.