Vazamento de memória com RxJS Observables

Clayton K. N. Passos
codigorefinado
Published in
3 min readJan 24, 2018

Angular usa RxJS como um pilar, que por sua vêz utiliza o conceito de Observable e Observers, onde Observable é oque pode ser observado, ou a fonte de dados, enquanto que Observer é que observa, ou recebe os dados. Lidar com Observables pode ser perigoso porque existe a possibilidade de criar um vazamento de memória. Como isso é possível?

Problema

Sempre que um componente ou diretiva é destruido, todos os “subscriptions” de Observables permanecem ativos.

Solução

Manualmente desinscrever de todos os Observables customizados quando um componente ou diretiva é destruído. O melhor lugar para fazer o “unsubscribe” é na função utilizando o ciclo de vida OnDestroy. Alguns não precisam deste cuidado, como o router e o http, para o resto há outras soluções:

  • executar “unsubscribe” após o subscription;
  • usar o operador takeUntil
  • usar async pipe

Unsubscribe

export class UnsubscribeCardComponent implements OnInit, OnDestroy {
message: string;
subscription: Subscription;
constructor(private upperCaseService: UpperCaseService) {}

ngOnInit() {
this.subscription = this.upperCaseService.getUpperCaseMessage()
.subscribe((message: string) => this.message = message);
}

ngOnDestroy(): void {this.subscription.unsubscribe();}
}

TakeUntil

http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-takeUntil
export class TakeUntilCardComponent implements OnInit, OnDestroy {
message: string;
private unsubscribe$ = new Subject();
constructor(private upperCaseService: UpperCaseService) {}

ngOnInit() {
this.upperCaseService.getUpperCaseMessage()
.takeUntil(this.unsubscribe$)
.subscribe((message: string) => this.message = message);
}

ngOnDestroy(): void {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
}

TakeUntil leva um segundo Observable como um argumento, ele monitora a segunda assinatura Observable e descarta depois que ele emite um valor ou termina.

takeWhile

Outra opção para realizar o unsubscribe é o utilizar o takeWhile

export class MyComponent implements OnDestroy, OnInit {

public user: User;
private alive: boolean = true;

public ngOnInit() {
this.userService.authenticate(email, password)
.takeWhile(() => this.alive)
.subscribe(user => {
this.user = user;
});
}

public ngOnDestroy() {
this.alive = false;
}

}

Subscribe.add

AsyncPipe

export class AsyncPipeCardComponent implements OnInit {
messageSubscription: Observable<string>;
constructor(private upperCaseService: UpperCaseService) {}

ngOnInit() {
this.messageSubscription = this.upperCaseService.getUpperCaseMessage();
}
}
<h4 class="card-title">{{messageSubscription | async}}</h4>

Quando o componente é destruido, o async pipe é desinscrito (unsubscribes) automáticamente. Mas cuidado, apesar de conveniente, o async pipe pode levar sua aplicação a uma lentidão.

Conclusão

Quando componentes e diretivas são destruídos todos os Observables precisam ser “desinscritos” (unsubscribed) manualmente. Async pipe é uma boa solução, porque faz tudo sozinho. O desenvolvedor líder da RxJS Ben Lesh recomenda usar o takeUntil, ele explica neste artigo.

Código de exemplo aqui.
Demo aqui.

Mais sobre RxJS

Texto original:

Recomendação de leitura

Build Reactive WebSites with RxJS

Reactive Programming with RxJS 5: Untangle Your Asynchronous JavaScript Code

https://stackoverflow.com/questions/38008334/angular-rxjs-when-should-i-unsubscribe-from-subscription/41177163

Quanto mais melhor

--

--