Is it necessary to unsubscribe from observables created by Http methods?

AngularMemory LeaksRxjsAngular2 Http

Angular Problem Overview


Do you need to unsubscribe from Angular 2 http calls to prevent memory leak?

 fetchFilm(index) {
        var sub = this._http.get(`http://example.com`)
            .map(result => result.json())
            .map(json => {
                dispatch(this.receiveFilm(json));
            })
            .subscribe(e=>sub.unsubscribe());
            ...

Angular Solutions


Solution 1 - Angular

So the answer is no, you don't. Ng2 will clean it up itself.

The Http service source, from Angular's Http XHR backend source:

enter image description here

Notice how it runs the complete() after getting the result. This means it actually unsubscribes on completion. So you don't need to do it yourself.

Here is a test to validate:

  fetchFilms() {
    return (dispatch) => {
        dispatch(this.requestFilms());

        let observer = this._http.get(`${BASE_URL}`)
            .map(result => result.json())
            .map(json => {
                dispatch(this.receiveFilms(json.results));
                dispatch(this.receiveNumberOfFilms(json.count));
                console.log("2 isUnsubscribed",observer.isUnsubscribed);
                window.setTimeout(() => {
                  console.log("3 isUnsubscribed",observer.isUnsubscribed);
                },10);
            })
            .subscribe();
        console.log("1 isUnsubscribed",observer.isUnsubscribed);
    };
}

As expected, you can see that it is always unsubscribed automatically after getting the result and finishing with the observable operators. This happens on a timeout (#3) so we can check the status of the observable when it's all done and completed.

And the result

enter image description here

So, no leak would exist as Ng2 auto unsubscribes!

Nice to mention: This Observable is categorized as finite, on contrary to the infinite Observablewhich is an infinite stream of data can be emitted like DOM click listener for example.

THANKS, @rubyboy for help on this.

Solution 2 - Angular

What are you people talking about!!!

OK so there's two reasons to unsubscribe from any observable. Nobody seems to be talking much about the very important second reason!

> 1) Clean up resources. As others have said this is a negligible problem for HTTP observables. It'll just clean itself up.

> 2) Prevent the subscribe handler from being run.

Note that with an HTTP observable, when you unsubscribe it will cancel the underlying request in your browser for you (you'll see 'Cancelled' in red in Network panel). This means time won't be wasted downloading or parsing the response. But that's actually an aside to my main point below.

The relevance of number 2 is going to depend upon what your subscribe handler does:

> If your subscribe() handler function has any kind of side effect that is undesired > if whatever calls it is closed or disposed then you must unsubscribe (or add conditional logic) to prevent it from being executed.

Consider a few cases:

  1. A login form. You enter username and password and click 'Login'. What if the server is slow and you decide to hit Escape to close the dialog? You'll probably assume you weren't logged in, but if the http request returned after you hit escape then then you will still execute whatever logic you have there. This may result in a redirect to an account page, an unwanted login cookie or token variable being set. This is probably not what your user expected.

  2. A 'send email' form.

If the subscribe handler for 'sendEmail' does something like trigger a 'Your email is sent' animation, transfer you to a different page or tries to access anything that has been disposed you may get exceptions or unwanted behavior.

Also be careful not to assume unsubscribe() means 'cancel'. Once the HTTP message is in-flight unsubscribe() will NOT cancel the HTTP request if it's already reached your server. It will only cancel the response coming back to you. And the email will probably get sent.

If you create the subscription to send the email directly inside a UI component then you probably would want to unsubscribe on dispose, but if the email is being sent by a non-UI centralized service then you probably wouldn't need to.

  1. An Angular component that is destroyed / closed. Any http observables still running at the time will complete and run their logic unless you unsubscribe in onDestroy(). Whether the consequences are trivial or not will depend upon what you do in the subscribe handler. If you try to update something that doesn't exist anymore you may get an error.

Sometimes you may have some actions you would want if the component is disposed, and some you wouldn't. For example maybe you have a 'swoosh' sound for a sent email. You'd probably want this to play even if the component was closed, but if you try to run an animation on the component it would fail. In that case some extra conditional logic inside subscribe would be the solution - and you would NOT want to unsubscribe the http observable.

So in answer to the actual question, no you don't need to do it to avoid memory leaks. But you need to do it (often) to avoid unwanted side effects being triggered by running code that may throw exceptions or corrupt your application state.

Tip: The Subscription contains a closed boolean property that may be useful in advanced cases. For HTTP this will be set when it completes. In Angular it might be useful in some situations to set a _isDestroyed property in ngDestroy which can be checked by your subscribe handler.

Tip 2: If handling multiple subscriptions you can create an ad-hoc new Subscription() object and add(...) any other subscriptions to it - so when you unsubscribe from the main one it will unsubscribe all the added subscriptions too.

Solution 3 - Angular

Unsubscribing is a MUST if you want a deterministic behavior on all network speeds.

Imagine that component A is rendered in a tab - You click a button to send a 'GET' request out. It takes 200 ms for the response to come back. So, you are safe to close the tab at any moment knowing that, the machine will be faster than you & the http response is processed and is complete before the tab is closed and component A is destroyed.

How about on a very slow network? You click a button, the 'GET' request takes 10 seconds to receive its response, but 5 seconds into waiting you decide to close the tab. That will destroy component A to be garbage-collected at a later time. Wait a minute!, we did not unsubscribe -- now 5 seconds later, a response comes back and the logic in the destroyed component will be executed. That execution is now considered out-of-context and can result in many things including very low performance and data/state corruption.

So, the best practice is to use takeUntil() and unsubscribe from http call subscriptions when the component is destroyed.

Note:

  • RxJS is not Angular specific
  • Angular async pipe used in templates unsubscribes on destroy automatically
  • Unsubscribing more than once has no negative side effect, except the extra no-op calls
import { Component, OnInit, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

interface User {
  id: string;
  name: string;
  age: number;
}

@Component({
  selector: 'app-foobar',
  templateUrl: './foobar.component.html',
  styleUrls: ['./foobar.component.scss'],
})
export class FoobarComponent implements OnInit, OnDestroy {
  private user: User = null;
  private destroy$ = new Subject();

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http
      .get<User>('api/user/id')
      .pipe(takeUntil(this.destroy$))
      .subscribe(user => {
        this.user = user;
      });
  }

  ngOnDestroy(): void {
    this.destroy$.next();  // trigger the unsubscribe
    this.destroy$.complete(); // finalize & clean up the subject stream
  }
}

Solution 4 - Angular

Calling the unsubscribe method is rather to cancel an in-progress HTTP request since this method calls the abort one on the underlying XHR object and remove listeners on the load and error events:

// From the XHRConnection class
return () => {
  _xhr.removeEventListener('load', onLoad);
  _xhr.removeEventListener('error', onError);
  _xhr.abort();
};

That said, unsubscribe removes listeners... So it could be a good idea but I don't think that it's necessary for a single request ;-)

Hope it helps you, Thierry

Solution 5 - Angular

After a while of testing, reading documentation and the sourcecode of the HttpClient.

HttpClient: https://github.com/angular/angular/blob/master/packages/common/http/src/client.ts

HttpXhrBackend : https://github.com/angular/angular/blob/master/packages/common/http/src/xhr.ts

HttpClientModule: https://indepth.dev/exploring-the-httpclientmodule-in-angular/

Angular Univeristy: https://blog.angular-university.io/angular-http/

> This particular type of Observables are single-value streams: If the HTTP request is successful, these observables will emit only one value and then complete

And the answer to the whole Issue of "do i NEED" to unsubscribe ?

It depends. Http call Memoryleaks are not a issue. The issues are the logic in your callback functions.

For example: Routing or Login.

If your call is a login call, you don't have to "unsubscribe" but you need to make sure if the User leaves the page, you handle the response properly in the absence of the user.


this.authorisationService
      .authorize(data.username, data.password)
      .subscribe((res: HttpResponse<object>) => {
          this.handleLoginResponse(res);
        },
        (error: HttpErrorResponse) => {
          this.messageService.error('Authentication failed');
        },
        () => {
          this.messageService.info('Login has completed');
        })

From annoying to dangerous

Now just imagine, the network is slower than usual, the call takes longer 5 seconds, and the user leaves the login view and goes to a "support view" .

The component may not be active but the subscription. In case of a response, the user will be suddenly rerouted (depending on your handleResponse() implementation).

This is not good.

Also just imagine the user leaves the pc, believing he is not logged in yet. But you logic logs the user in, now you have a security issue.

What can you do WITHOUT unsubscribing?

Make you call dependent on the current state of the view:

  public isActive = false;
  public ngOnInit(): void {
    this.isActive = true;
  }

  public ngOnDestroy(): void {
    this.isActive = false;
  }

User .pipe(takeWhile(value => this.isActive)) to make sure the response is only handled when the view is active.


this.authorisationService
      .authorize(data.username, data.password).pipe(takeWhile(value => this.isActive))
      .subscribe((res: HttpResponse<object>) => {
          this.handleLoginResponse(res);
        },
        (error: HttpErrorResponse) => {
          this.messageService.error('Authentication failed');
        },
        () => {
          this.messageService.info('Login has completed');
        })

But how can you be sure that the subscription isn't causing memoryleaks ?

You can log if the "teardownLogic" is applied.

The teardownLogic of a subscription will be called when the subcription is empty or unsubscribed.


this.authorisationService
      .authorize(data.username, data.password).pipe(takeWhile(value => this.isActive))
      .subscribe((res: HttpResponse<object>) => {
          this.handleLoginResponse(res);
        },
        (error: HttpErrorResponse) => {
          this.messageService.error('Authentication failed');
        },
        () => {
          this.messageService.info('Login has completed');
    }).add(() => {
        // this is the teardown function
        // will be called in the end
      this.messageService.info('Teardown');
    });

You don't have to unsubscribe. You should know if there are issues in your logic, that could cause Problems in your subscription. And take care of them. In the most cases, it won't be a issue, but especcialy at critical tasks like autorization, you should take care of unexpected behaviour, wether its with "unsubscribe" or a other logic like piping or conditional callback functions.

why not just always unsubscribe?

Imagine you make a put or post request. The server recieves the message either way, just the response takes a while. Unsubscribing, won't undo the post or put. But when you unsubscribe, you won't have the chance to handle the response or inform the User, for example via Dialog or a Toast/Message etc.

Wich causes the User to believe, that the put/post request was not done.

So it depends. It is your design decision, how to deal with such issues.

Solution 6 - Angular

Also with the new HttpClient module, remains the same behaviour packages/common/http/src/jsonp.ts

Solution 7 - Angular

You should definitely read this article. It shows you why you should always unsubscribe even from http.

> If after creating the request but before receiving an answer from the > back-end you deem the component unnecessary and destroy it, your > subscription will maintain the reference to the component thus > creating a chance for causing memory leaks.

Update

The above affirmation seems to be true, but anyway, when the answer comes back the http subscription is destroyed anyway

Solution 8 - Angular

You shouldn't unsubscribe from observables that completes automatically (e.g Http, calls). But it's necessary to unsubscribe from infinite observables like Observable.timer().

Solution 9 - Angular

A good part that could help understand this is that an HTTP request isn't made unless there is a call to the subscribe function. Though the answers on this page seem to suggest two different practices, it really doesn't makes much of a difference as the required behavior would be controlled by an async pipe, as indicated by the angular docs (though it's mentioned much later in the section 'Making a DELETE request'):

The AsyncPipe subscribes (and unsubscribes) for you automatically.

In fact, it's even harder to find any example in docs where such observables are explicitly unsubscribed by calling the unsubscribe function.

Solution 10 - Angular

I've built an entire huge corporate app under the belief that httpclient gets unsubscribed automatically and I haven't encountered any memory leaks whatsoever.

Solution 11 - Angular

RxJS observable are basically associated and work accordingly you subscribe it. When we create the observable and the movement we complete it, observable automatically gets closed and unsubscribed.

They work in the same fashion of the watchers but quite different order. The better practice to unsubscribe them when component is getting destroy.We could subsequently do that by eg. this.$manageSubscription.unsubscibe()

If we have created the observable like below mentioned syntax such as

** **return new Observable((observer) => { ** // It makes observable in cold state ** observer.complete() }) **

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionborn2netView Question on Stackoverflow
Solution 1 - Angularborn2netView Answer on Stackoverflow
Solution 2 - AngularSimon_WeaverView Answer on Stackoverflow
Solution 3 - Angularun33kView Answer on Stackoverflow
Solution 4 - AngularThierry TemplierView Answer on Stackoverflow
Solution 5 - AngularLuxusproblemView Answer on Stackoverflow
Solution 6 - AngularRicardo MartínezView Answer on Stackoverflow
Solution 7 - AngularMTZView Answer on Stackoverflow
Solution 8 - AngularMykhailo MykhaliukView Answer on Stackoverflow
Solution 9 - AngularNaman BakhruView Answer on Stackoverflow
Solution 10 - AngularAndrew Junior HowardView Answer on Stackoverflow
Solution 11 - AngularSachin MishraView Answer on Stackoverflow