Angular

Unsubscribing From an Observable in Angular

Since version 2.0, Angular uses RX.js observables a lot. Such an observable is potentially infinite. So in many cases, you need to unsubscribe from the observable manually. If you forget to do so, you end up with a memory leak.

Actually, you’ll probably get away without unsubscribing for a long time. First, Angular cares about unsubscribing in most cases:

  • The async pipe unsubscribes automatically for you.
  • HTTP requests are short-lived observables. They call onComplete or onError after returning the result, so they unsubscribe automatically.
  • The same applies to every finite observable, such as Observable.timer(1000).subscribe(...);.
  • You don’t have to unsubscribe from @HostListener and ActivatedRoute.

However, even the official documentation of Angular recommends unsubscribing nonetheless, because it’s little effort and you never know. For instance, there seems to be a bug preventing ActivatedRoute from unsubscribing automatically under certain circumstances. You might also destroy finite Observables (such as HTTP requests) because you don’t want them to be processed if the component has already been destroyed. IMHO that’s no memory leak, but it’s useless to continue loading data after the user has pressed the “back” button.

Unsubscribing imperatively

There’s that. The ugly side of observables is that it’s a lot of boiler-plate code to unsubscribe. You need to store a reference to the subscription in the component or service using it, and you have to add the unsubscribe code in the onDestroy method:

export class UserComponent implements OnDestroy {

  private timer$: Subscription;

  private constructor() {
      this.timer$ = Observable.interval(1000).subscribe(...); 
  }

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

Call complete() instead of unsubscribe()

It’s a good idea to call complete() prior to unsubscribing, just in case there’s some tidy-up code. According to StackOverflow, you usually don’t have to unsubscribe after completing the observable. If you’re uncertain, check it by calling isUnsubscribed().

Getting rid of the boilerplate code

Still, there’s all that boiler-plate code. The RX.js operator takeUntil provides us an opportunity to get rid of it. ng2-rx-componentdestroyed is a small NPM library using this approach. All you have to do is to add a (potentially empty) onDestroy method and a clever takeUntil call:

@Component({
  selector: 'foo',
  templateUrl: './foo.component.html'
})
export class FooComponent implements OnInit, OnDestroy {

  ngOnInit() {
    Observable.interval(1000)
        .pipe(
            untilComponentDestroyed(this) // <--- magic is here!
        )
        .subscribe(console.log);
  }

  ngOnDestroy() {
  }  
}

Copyright hint: demo has been copied from https://github.com/w11k/ng2-rx-componentdestroyed

Just in case you’re missing the call to takeUntil: untilComponentDestroyed() is a short-hand form of takeUntil(componentDestroyed(this)).

Read the full story about ng2-rx-componentdestroyed at the announcement article written by Roman Roelofsen.

Alternative approach using a decorator

If you prefer to use a decorator instead of adding an empty onDestroy method, have a look at Paweł Giemza’s library:

@DestroyableComponent
@Component({
  selector: 'foo',
  templateUrl: './foo.component.html'
})
export class FooComponent implements OnInit {

  ngOnInit() {
    Observable.interval(1000)
        .pipe(
            .takeUntil(componentDestroyed(this)) // <--- magic is here!
        )
        .subscribe(console.log);
  }
}

It looks a bit cleaner to me, but you still have to edit code in two places, so little is won. It seems to be mostly a matter of taste.

Paweł’s library is available at NPM under an MIT license, but I didn’t find the source code yet.

Wrapping it up

Angular automatically unsubscribes observables so often it’s easy to forget unsubscribing altogether. Luckily, using one of the small helper libraries, unsubscribing is not a big deal, so we can start to unsubscribe every observable by default without a lot of effort.


Dig deeper

RX.js – don’t unsubscribe by Ben Lesh
When to unsubscribe in Angular by Netanel Basal
The announcement article written by Roman Roelofsen
Using the takeUntil RxJS Operator to Manage Subscriptions Declaratively
Paweł Giemza’s announcement post