Brian Love
Angular + TypeScript Developer in Denver, CO

Angular: Don't forget to unsubscribe()

Reading time ~5 minutes

Don’t forget to unsubscribe from subscriptions in Angular components using the OnDestroy lifecycle hook. We’ll look at:

  1. Why you need to unsubscribe
  2. When you likely should unsubscribe
  3. How you unsubscribe

Updated January 4, 2017

I have updated this post to include the use of the takeWhile() operator as a better strategy for unsubscribing in your Angular components.

Updated September 13, 2017

I have updated this post to include the use of the takeUntil() operator as another strategy for unsubscribing in an Angular component.

Why?

Reactive-Extensions for JavaScript (or RxJS) introduces the concept of Observables to Angular. If you have been using version 1 of Angular then you are likely comfortable using Promises. And, while you might think that an Observable is just like a Promise you might be surprised (as I was) to learn that they are in fact very different.

First, Promises are eager and are executed immediately. Observables are not eager and are only executed when subscribed to.

Second, Promises are asynchronous. Observables can be either synchronous or asynchronous.

Third, Promises are expected to return only a single value (like a function). Observables can return zero, one or more (infinitely) values.

Let me get to the point. A subscription is created when we subscribe() to an observable. And it is important that we unsubscribe from any subscriptions that we create in our Angular components to avoid memory leaks.

AsyncPipe

Before we go any farther; if you are using observable streams via the AsyncPipe then you do not need to worry about unsubscribing. The async pipe will take care of subscribing and unsubscribing for you.

When?

Ok, we covered the why - but, when do we unsubscribe? In most instances the best time to unsubscribe is when your component is destroyed. Angularintroduced the the ngOnDestroy() lifecycle method:

ngOnDestroy(): Cleanup just before Angular destroys the directive/component. Unsubscribe observables and detach event handlers to avoid memory leaks.

How?

There are three common approaches:

  1. Store the subscription and invoke the unsubscribe() method when the component is destroyed.
  2. Use the takeWhile() operator to complete the subscription.
  3. Use the takeUntil() operator to complete the subscriptiuon.

unsubscribe()

First, let’s assume we have a UserService class with an authenticate() method:

@Injectable()
export class UserService {

  public authenticate(email: string, password: string): Observable<User> {
    //code omitted
  }
}

In our component we first import the OnDestroy class from @angular/core as well as the ISubscription interface from rxjs:

import { OnDestroy } from "@angular/core";
import { ISubscription } from "rxjs/Subscription";

Next, we implement the abstract class and declare a variable to store a reference to our subscription:

export class MyComponent implements OnDestroy {

  private subscription: ISubscription;

}

Next, let’s subscribe to the observable returned from the UserService.authenticate() method. In this example I have a method that is invoked when a login form is submitted:

public onSubmit() {
  //authenticate the user via the UserService
  this.subscription = this.userService.authenticate(email, password).subscribe(user => {
    this.user = user;
  });
}

Finally, when our component is destroyed we unsubscribe:

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

If we are subscribing to multiple subscriptions we do not need to use a collection to store all of the subscription instances. Rather, a subscription can have child subscriptions. Simply invoke the add() method to add additional child subscriptions. This is helpful, as all child subscriptions will be unsubscibed when we unsubscribe() the parent subscription.

Let’s add an additional subscription:

getUser(id: string) {
  const subscription = this.userService.get(id)
    .subscribe(user => {
      this.user = user;
    });
  this.subscription.add(subscription);
}

In the code above we are getting an additional user by their id value from the REST API, and we are subscribing to the observable that is returned from the get() method. We simply add() this new subscription to our existing subscription object. Then, invoke the unsubscribe() method, just like we did before:

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

Now, both the primary subscription (to the authenticate() method) is unsubscribed, as well as the child subscription (to the get() method).

takeWhile()

Another approach to unsubscribing is to use the http://reactivex.io/documentation/operators/takewhile.html operator:

The TakeWhile mirrors the source Observable until such time as some condition you specify becomes false, at which point TakeWhile stops mirroring the source Observable and terminates its own Observable.

First, we need to import the takeWhile() operator:

import "rxjs/add/operator/takeWhile";

Then, let’s use the takeWhile() operator with our observable that is returned from UserService.authenticate():

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;
  }

}

First, I defined a private boolean variable named alive that is set to true. Next, we provide a function to the takeWhile() operator that returns the boolean value (that is initially true). Finally, we set the value of alive to false when the component is destroyed.

As you can see, the takeWhile() operator is an excellent solution to unsubscribing from an observable subscription as part of Angular’s component lifecycle.

takeUntil()

Similar to the takeWhile() operator, the takeUntil() will emit values until the provided observable emits a value. So, while the takeWhile() operator will emit values while the boolean value provided is true, the takeUntil() operator will emit values until a provided observable emits a value.

From the documentation:

The TakeUntil subscribes and begins mirroring the source Observable. It also monitors a second Observable that you provide. If this second Observable emits an item or sends a termination notification, the Observable returned by TakeUntil stops mirroring the source Observable and terminates.

Let’s see this in action:

import "rxjs/add/operator/takeUntil";
import { Subject } from "rxjs/Subject";

export class MyComponent implements OnDestroy, OnInit {

  public user: User;
  private unsubscribe: Subject<void> = new Subject(void);

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

  public ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
  }

}

As you can see, this approach is very similar to using the takeWhile() operator.

A few things to note:

  • First, we import the takeUntil() operator as well as the Subject class.
  • Next, we define a private instance property named unsubscribe, which is a Subject. We also create a new instance of Subject, defining the generic type as void.
  • We use the takeUntil() operator before invoking subscribe(), providing the unsubscribe observable.
  • In the ngOnDestroy() lifecycle method we emit a value via the next() method, and then complete() the observable. When the observable emits the value our subscriptions will be complete.

The takeUntil() is another solution for unsubscribing from observables.

Which method?

The question you might be asking yourself at this point is:

Which method should I use to unsubscribe?

First, I would recommend that you use the async pipe. It makes sense to abstract this away and to let the Angular framework handle the subscriptions for you.

If using the async pipe is not a good solution for your needs, then I would recommend either using the takeWhile() or the takeUntil() method, whichever is more convenient for you and your application.

Brian Love

Hi, I'm Brian. I am interested in TypeScript, Angular and Node.js. I'm married to my best friend Bonnie, I live in Denver and I ski (a lot).