by Milos Brdar

Angular uses RxJS as a backbone of the Angular application. RxJS uses the concept of Observables and Observers, where an Observable is a source of data and Observer is the one who use the data. Every time an Observable produces new values, it informs an Observer and the Observer handles those values inside subscribe operator. Dealing with Observables can be dangerous because there is the possibility of creating a memory leak. How’s that even possible?

Problem

Every time a component/directive is destroyed, all subscriptions to custom Observables still remain active.

Solution

Manually unsubscribe from all custom Observables when a component/directive gets destroyed. The best place to unsubscribe is inside functions that handle the OnDestroy lifecycle hook. Some subscriptions like router and http don’t need manual unsubscribe, for the rest of them there are various solutions:

execute unsubscribe over the subscription object

using takeUntil operator

using 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 ( ) ; } } 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();} }

Subscription represents a disposable resource, it has an unsubscribe method that can be used to dispose the resource held by the subscription.

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 ( ) ; } } 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 takes a second Observable as an argument, it monitors the second Observable and discard subscription after it emits a value or terminates.

AsyncPipe

export class AsyncPipeCardComponent implements OnInit { messageSubscription : Observable < string >; constructor ( private upperCaseService : UpperCaseService ) { } ngOnInit ( ) { this . messageSubscription = this . upperCaseService . getUpperCaseMessage ( ) ; } } 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> <h4 class="card-title">{{messageSubscription | async}}</h4>

The async pipe subscribes to an Observable and returns the latest value it has emitted. When a component is destroyed, the async pipe unsubscribes automatically.

Conclusion

When a component/directive is destroyed, all custom Observables need to be unsubscribed manually. Async pipe is a good solution, because it does everything automatically, but keep in mind that it can’t be used in all scenarios.

The RxJS lead developer Ben Lesh recommends the takeUntil approach. He explains his arguments in this article.

The sourcecode for the complete example can be found here.

The demo example can be found here.