50

What is the best practice to unsubscribe within a Angular2 service from a http subscription?

Currently I do this but I'm not sure if this will be the best way.

import { Injectable } from "@angular/core";
import { Http } from "@angular/http";

import { Subject } from "rxjs/Subject";
import { ISubscription } from "rxjs/Subscription";

@Injectable()
export class SearchService {
    private _searchSource = new Subject<any>();

    public search$ = this._searchSource.asObservable();

    constructor(private _http: Http) {}

    public search(value: string) {
        let sub: ISubscription = this._http.get("/api/search?value=" + value)
            .map(response => <any>response.json())
            .do(data => this._searchSource.next(data))
            .finally(() => sub.unsubscribe()).subscribe();
    }

}
NCC-2909-M
  • 709
  • 1
  • 7
  • 15

4 Answers4

142

A Service in Angular is a singleton. This means that the service will exist for the entire lifespan of your application.

The reason that you need to unsubscribe from an observable, is to avoid memory leaks. When do you get memory leaks? If something has been garbage collected while it was still subscribed to an observable, event listener, socket, ...

Since an Angular service never get's destroyed, unless your entire application get's destroyed, there is no real reason to unsubscribe from it. The observable will either complete or error or keep going as long as your application does.

Conclusion: Unsubscribing in a service is kind of pointless, since there is no chance of memory leaks.

KwintenP
  • 4,637
  • 2
  • 22
  • 30
  • Thanks for the answer @kwintenp. But as you see above, Every call of the search method will create a new observable which I have to subscribe to get the data. Didn't I have to unsubscribe the subscription to avoid memory leaks? – NCC-2909-M Nov 30 '16 at 11:15
  • 2
    That is ok. Every subscription returned will clean themselves up. The HTTP service cleans up after itself. When the XHR result is received, the subscription’s complete method is called and therefore unsubscribes itself. However, there could be a case for calling the unsubscribe method on the http service’s returned Observable. If we ever wanted to cancel our on going HTTP request we would call unsubscribe. This calls abort under the covers to complete the clean up. – Thibs Nov 30 '16 at 19:09
  • 48
    "the service will exist for the entire lifespan of your application" is not true. I can provide a service just for the lifetime of a component, so that it gets destroyed when the component gets destroyed... – Markus Ende Jun 09 '17 at 06:28
  • 13
    "Conclusion: Unsubscribing in a service is kind of pointless, since there is no chance of memory leaks." I think it's more accurate to say that unsubscribing to observables created by angular's http service is not needed. There could be a situation where a service shared by two components references some object inside the subscription. If that shared service gets garbage collected (see @MarkusE. comment above) you can end up with a memory leak. – instantaphex Jul 18 '17 at 14:25
  • If some observables shared between components that are exposed through an Angular service, for example, they may be subscribed multiple times as long as the component is initialized. So we must always unsubscribe from them. – Mouneer Aug 07 '18 at 18:37
  • Are we worried about memory usage at all? – Ben Racicot Apr 30 '19 at 17:41
  • 1
    I know this is an old thread, but felt like adding this cause I came here seeking information on how to unsubscribe from observables inside a service when they aren't singletons (specially). I stick to the format of returning the observable as part of the response interface, for the calling component to be able to unsubscribe to it whenever it wishes. Hope this helps. – automaticAllDramatic Apr 20 '20 at 12:49
  • This is a terrible answer, which is wrong in almost any way it can be. In fairness, Angular's changed a lot since 2016, but that's not the only reason it's wrong. Look at the @kfir-erez answer for the correct one: https://stackoverflow.com/a/57476730/127826 – Cobus Kruger Mar 29 '23 at 09:42
22

I disagree with KwintenP answer. Yes in case of observable to HttpClient call there is no need to unsubscribe as Vladimir mentioned correctly however in other observables you may definitely need to unsubscribe in a service.

Let see simple example: Assume we have a store that send observables and in the store we have a clicker observable that fire true whenever there is a click on the right mouse (for some weird reason) And assume we have MyWeirdService that do the following:

class MyWeirdService {
  doSomethingUnthinkableManyTimes() {
    this.store.select('clicker').subscribe(() => {
      console.log("Hey what do you know, I'm leaking");
    });
  }
}

this.store.select('clicker') returns an observable that we registering a new handler to it on every call to doSomethingUnthinkableManyTimes without cleaning it resulting in memory leak that will stay as long as the service is there (application lifetime in many cases)

Bottom line you don't need to unsubscribe in the case above of Http as Vladimir explained it well but in other cases you may need it.

-------------- Edition ------------

To solve that issue in my example, just add take(1) and it will unsubscribe automatically after each stream is fired:

class MyWeirdService {
  doSomethingUnthinkableManyTimes() {
    this.store.select('clicker')
     .pipe(take(1))
     .subscribe(() => {
      console.log("What a relief, I'm not leaking anymore");
     });
  }
}
Kfir Erez
  • 3,280
  • 2
  • 18
  • 17
  • 1
    This is Awesome, Kfir Erez should have went through the rough road of observable unsubscription issue in services. I have found your answer to be very helpful. @KwintenP I would recommend this to be the answer for the question to not mislead others – Tadele Ayelegn Oct 16 '22 at 05:51
8

You don't need to unsubscribe from observable created by Http or HttpClient because it is finite observable (value will be emitted only once and complete will be called).

However, you CAN unsubscribe from the observable created by HttpClient to cancel the request. It mean you are no longer interested in data returned by the request.

Vladimir Prudnikov
  • 6,974
  • 4
  • 48
  • 57
0

You can do this:

You need to understand that the service file should be used to just define the http methods and not subscribe there itself. 
Create the method in the service file, use Dependency injection to inject that service in the component and then use ngOnDesteoy to kill the subscription 

****** this is in your component.ts file *******
// introduce a new subject variable to destroy the subscription
destroy$: Subject<any> = new Subject();

constructor(private serviceName: yoirService){} // Dependency injection 

// wherever you want to unsubsribe an observable or a subscription
this.serviceName.observableName.pipe(takeUntil(this.destroy$)).subscribe(
    // required code 
);

//  use ngOnDestroy() to kill it
ngOnDestroy() {
   this.destroy$.next();
   this.destroy$.complete();
}

This way you'll destroy the service once the component is destroyed. 

Srikar Phani Kumar M
  • 1,069
  • 1
  • 3
  • 8