Do I need to unsubscribe from an Observable?

development

Do I need to unsubscribe from an Observable?

RxJs code

When using RxJS with Angular 2 you can often avoid calling .subscribe() on Observables because the framework does a lot for you. Look at the AsyncPipe for example, which both subscribes and unsubscribes for you.

But what about when you manually call .subscribe() on some Observable in your component? I asked myself this same question and did some research to find a reasonable answer.

Finite and infinite

Observables produce/emit values over time but in answering this question it can be helpful to categorize Observables into two groups:

finite values and infinite values.

Finite Observables are ones that will stop emitting values at some point before the application ends, whereas infinite ones will not stop until the end of time (when you close your browser tab).

Clicks and requests

Listening for events

JavaScript event listeners, like a listener to mouse clicks, are an allocated resource (read memory) in the web world. We need to manage these resources carefully so as not to create memory leaks. If you create one of these listeners it won’t just go away after a minute, an hour, or ever as long as the application is running.

Clicks can continue to be captured and your function will be called. This make sense because the emitting of the mouse click event is totally up to the user and the user will decide when they stop clicking, not the app.

Let’s place DOM event listeners into the infinite value Observables group. If we register this event listener in our component by calling something like Observable.fromEvent() and then the user navigates away or somehow causes the component to be destroyed, the listener will still be there.

JavaScript doesn’t know that just because our component is no longer there we do not care about the events. When the user clicks the element, the event will still be emitted but nothing will respond to it. If the component is re-created a new event listener will be created by the call to Observable.fromEvent().

These event listeners will continue to pile up and eat up memory.

  We can stop this nasty scenario by calling
  `.unsubscribe()` on the subscription returned by the
  Observable. This call can be made anywhere but a convenient place for it
  is in the `ngOnDestroy()` method of your component.


  By doing this you know when the component is destroyed the resources
  allocated for the event listener will also be freed and this clean up will
  not be dependent on any of your component's logic or state.

What about http requests?

Http requests are a different type of thing from an event listener. By their very nature they are finite. You make one request and get one response. But did you know that XMLHttpRequests also use event listeners?!

function reqListener() {
  console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener('load', reqListener);
oReq.open('GET', 'http://www.example.org/example.txt');
oReq.send();

Source: MDN

With $http requests and Promises in AngularJs you know that your .finally() method will always be called but you never worried about un-promising or cleaning up that event listener. That’s because the framework handled it for you. Likewise with Observables and RxJS everything is handled for you as long as the stream of values completes.

This is the key different between finite value Observables and infinite valued ones.

Angular 2, **normal http requests will always return one value and then complete**.

They will always complete, even if you get a 404 or something else when the request fails, just like your .finally() method was always called with Promises in AngularJs.

When an RxJS Observable completes it cleans itself and all of its allocated resources up. Even if you navigate away from the component before the http response arrives and the component is destroyed the Observable will still complete and clean up there just won’t be anyone around to respond to it (I could probably say something about a tree in the forest here).

You do not need to .unsubscribe() from http requests. But it might be a good practice anyway!

Another example

Imagine inside a component you .subscribe() to some Observable that emits 10 values with a delay of 10 seconds between each value. If that component is destroyed after 5 seconds, the Observable chain will still emit values for another 95 and at that point the interval used internally by RxJS to produce the 10 second delay will be cleaned up.

You could call subscription.unsubscribe() in the ngOnDestroy() method to clean up early and reclaim some memory 95 seconds early but since this Observable will emit a finite number of values you do not have to worry about a permanent allocation of resources due to your missing .unsubscribe().

Cleanup and unsubscribe()

I hope this was a clear explanation of a topic I found quite a few people asking while searching for an answer myself. I detailed this topic originally in a StackOverflow answer where I provide some citations from sources much more reputable than myself verifying this answer.