Quando começamos o aprendizado de uma aplicação Angular, aprendemos o básico de RxJS para criar nossos componentes, principalmente quando temos que realizar alguma requisição HTTP. ( link )
O conceito de Observable implementado no RxJS é poderoso e nos permite acessar um fluxo de dados (como uma requisição Http) de uma maneira fluída e organizada.
Nesse exemplo eu estou fazendo uma requisição na API e me inscrevendo (subscribe) no retorno para atribuir a um objeto que usarei na minha interface.
Porém, nesse simples exemplo mora um perigo não muito claro, a ameaça silenciosa do vazamento de memória.
Quando esse componente não for mais utilizado, como por exemplo se o usuário ir para outra página, o Observable gerado por essa requisição HTTP ainda estará ativo pois o componente não se desinscreveu nele.
Como o componente já foi destruído, esse Observable ainda ativo virou um vazamento de memória(memory leak).
Isso causa uma degradação do desempenho, aumento do consumo de recursos da máquina e principalmente, perda de tempo do desenvolvedor, pois em uma aplicação grande esse tipo de erro é bem complicado de se localizar.
Então, o que podemos fazer ? Simples: garantir que meu componente se desinscreva sempre que ele não precisar mais do Observable!
OK, mas como fazer isso? Ah! Para isso temos algumas maneiras…
A Básica.
Para se desinscrever de um Observable basta você seguir dois passos:
Guardar a inscrição: O método subscribe tem como retorno um objeto do tipo subscription que representa a inscrição realizada.
Chamar o método unsubscribe do objeto subscription.
Isso é simples, porém a questão é quando chamar o unsubscribe.
No Angular os componentes possuem métodos de ciclo de vida ( Lifecycle Hooks ), normalmente quando criamos via cli (ng g c) ele já vem de fábrica com o método OnInit.
Para desinscrever o componente do Observable antes dele ser destruído, podemos então utilizar o método OnDestroy.
Pronto vazamento eliminado!
Porém nem sempre nossos componentes terão apenas um Observable,assim essa solução simples pode ficar um pouco feia …
Veja que adicionamos mais um Observable e tivemos que guardar mais uma Subscription e realizar mais uma condição no método onDestroy.
Podemos melhorar?
O SubSink
Entra a biblioteca SubSink focada em facilitar a coleta e a desinscrição nos Observables.
Ela é muito simples e tem apenas um método e um atributo:
O atributo sink onde você adiciona a inscrição em uma lista que fica implícita.
O método unsubscribe que realiza a desinscrição de toda a lista do atributo sink.
Vamos ao exemplo anterior verificar como fica mais simples a administração dos Observables do componente.
Ainda temos mais uma alternativa. E essa já vem por padrão no Angular.
O pipe async.
Quando componentes utilizam Observables em seus templates HTML uma alternativa muito interessante é o pipe async ( | async).
Com ele o componente apenas passa para o template o observable. E o motor de renderização do Angular faz o trabalho de realizar a inscrição (subscribe) e atribuir o retorno a um objeto.
A melhor parte é que o Angular cuida da desinscrição para você!
Vamos ver como ficam o template e o componente:
Veja que não temos que chamar o OnDestroy, o framework faz isso para você!
Para mais informações, segue o vídeo da Loiane sobre o tema( vídeo ).
Conclusão
Nesse artigo vimos os cuidados que temos que ter para evitar vazamento de memória (memory leaks) nas nossa aplicações Angular.
Resumindo:
Se seu componente utiliza observables para apresentar informações no template, utilize o pipe async.
Se você tem apenas um observable onde você precise fazer a inscrição(subscribe), guarde o objeto Subscription em uma variável e se desinscreva no método OnDestroy do componente.
Caso seu componente tenha vários observables, utilize a bibiloteca SubSink para seu código ficar mais limpo.
Código fonte do exemplo( link ).
Obrigado e compartilhe esse artigo com seus colegas para eles conhecerem esses cuidados na construção de componentes Angular.