When should I store the Subscription instances and invoke unsubscribe() during the NgOnDestroy life cycle and when can I simply ignore them?

Saving all subscriptions introduces a lot of mess into component code.

HTTP Client Guide ignore subscriptions like this:

getHeroes() {
  this.heroService.getHeroes()
                   .subscribe(
                     heroes => this.heroes = heroes,
                     error =>  this.errorMessage = <any>error);
}

In the same time Route & Navigation Guide says that:

Eventually, we'll navigate somewhere else. The router will remove this component from the DOM and destroy it. We need to clean up after ourselves before that happens. Specifically, we must unsubscribe before Angular destroys the component. Failure to do so could create a memory leak.

We unsubscribe from our Observable in the ngOnDestroy method.

private sub: any;

ngOnInit() {
  this.sub = this.route.params.subscribe(params => {
     let id = +params['id']; // (+) converts string 'id' to a number
     this.service.getHero(id).then(hero => this.hero = hero);
   });
}

ngOnDestroy() {
  this.sub.unsubscribe();
}
9 upvote
  flag
I guess Subscriptions to http-requests can be ignored, as they only call onNext once and then they call onComplete. The Router instead calls onNext repeatedly and might never call onComplete (not sure about that...). Same goes for Observables from Events. So I guess those should be unsubscribed. – Springrbua
1 upvote
  flag
I'm having the same qeustions. My guess (someone please confirm): Looking at the 2 examples and reading the answer below.. In first example, no subscription is actually created within the component so nothing hangs around once it's complete and function goes out of scope. While in the 2nd example, a 'private sub: any' is created in the component and it wouldn't "complete" on its own so it needs to be unsubscribed?? – gt6707a
upvote
  flag
@gt6707a The stream completes (or does not complete) independent of any observation of that completion. The callbacks (the observer) provided to the subscription function do not determine if resources are allocated. It is the call to subscribe itself that potentially allocates resources upstream. – seangwright

11 Answers 11

Angular 2 official documentation provides an explanation for when to unsubscribe and when it can be safely ignored. Have a look at this link:

https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

Look for the paragraph with the heading Parent and children communicate via a service and then the blue box:

Notice that we capture the subscription and unsubscribe when the AstronautComponent is destroyed. This is a memory-leak guard step. There is no actual risk in this app because the lifetime of a AstronautComponent is the same as the lifetime of the app itself. That would not always be true in a more complex application.

We do not add this guard to the MissionControlComponent because, as the parent, it controls the lifetime of the MissionService.

I hope this helps you.

3 upvote
  flag
as a component you never know whether you're a child or not. therefore you should always unsubscribe from subscriptions as best practice. – SeriousM
1 upvote
  flag
The point about MissionControlComponent is not really about whether it's a parent or not, it's that the component itself provides the service. When MissionControl gets destroyed, so does the service and any references to the instance of the service, thus there is no possibility of a leak. – ender

It depends. If by calling someObservable.subscribe(), you start holding up some resource that must be manually freed-up when the lifecycle of your component is over, then you should call theSubscription.unsubscribe() to prevent memory leak.

Let's take a closer look at your examples:

getHero() returns the result of http.get(). If you look into the angular 2 source code, http.get() creates two event listeners:

_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);

and by calling unsubscribe(), you can cancel the request as well as the listeners:

_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();

Note that _xhr is platform specific but I think it's safe to assume that it is an XMLHttpRequest() in your case.

Normally, this is enough evidence to warrant a manual unsubscribe() call. But according this WHATWG spec, the XMLHttpRequest() is subject to garbage collection once it is "done", even if there are event listeners attached to it. So I guess that's why angular 2 official guide omits unsubscribe() and lets GC clean up the listeners.

As for your second example, it depends on the implementation of params. As of today, the angular official guide no longer shows unsubscribing from params. I looked into src again and found that params is a just a BehaviorSubject. Since no event listeners or timers were used, and no global variables were created, it should be safe to omit unsubscribe().

The bottom line to your question is that always call unsubscribe() as a guard against memory leak, unless you are certain that the execution of the observable doesn't create global variables, add event listeners, set timers, or do anything else that results in memory leaks.

When in doubt, look into the implementation of that observable. If the observable has written some clean up logic into its unsubscribe(), which is usually the function that is returned by the constructor, then you have good reason to seriously consider calling unsubscribe().

up vote 483 down vote accepted

--- Edit 3 - The 'Official' Solution (2017/04/09)

I spoke with Ward Bell about this question at NGConf (I even showed him this answer which he said was correct) but he told me the docs team for Angular had a solution to this question that is unpublished (though they are working on getting it approved). He also told me I could update my SO answer with the forthcoming official recommendation.

The solution we should all use going forward is to add a private ngUnsubscribe: Subject = new Subject(); field to all components that have .subscribe() calls to Observables within their class code.

We then call this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); in our ngOnDestroy() methods.

The secret sauce (as noted already by @metamaker) is to call .takeUntil(this.ngUnsubscribe) before each of our .subscribe() calls which will guarantee all subscriptions will be cleaned up when the component is destroyed.

Example:

import { Component, OnDestroy, OnInit } from '@angular/core';
import 'rxjs/add/operator/takeUntil';
// import { takeUntil } from 'rxjs/operators'; // for rxjs ^5.5.0 lettable operators
import { Subject } from 'rxjs/Subject';

import { MyThingService } from '../my-thing.service';

@Component({
    selector: 'my-thing',
    templateUrl: './my-thing.component.html'
})
export class MyThingComponent implements OnDestroy, OnInit {
    private ngUnsubscribe: Subject = new Subject();

    constructor(
        private myThingService: MyThingService,
    ) { }

    ngOnInit() {
        this.myThingService.getThings()
            .takeUntil(this.ngUnsubscribe)
            .subscribe(things => console.log(things));

        /* if using lettable operators in rxjs ^5.5.0
        this.myThingService.getThings()
            .pipe(takeUntil(this.ngUnsubscribe))
            .subscribe(things => console.log(things));
        */

        this.myThingService.getOtherThings()
            .takeUntil(this.ngUnsubscribe)
            .subscribe(things => console.log(things));

    }

    ngOnDestroy() {
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
    }
}

--- Edit 2 (2016/12/28)

Source 5

The Angular tutorial, the Routing chapter now states the following: "The Router manages the observables it provides and localizes the subscriptions. The subscriptions are cleaned up when the component is destroyed, protecting against memory leaks, so we don't need to unsubscribe from the route params Observable." - Mark Rajcok

Here's a discussion on the Github issues for the Angular docs regarding Router Observables where Ward Bell mentions that clarification for all of this is in the works.

--- Edit 1

Source 4

In this video from NgEurope Rob Wormald also says you do not need to unsubscribe from Router Observables. He also mentions the http service and ActivatedRoute.params in this video from November 2016.

--- Original Answer

TLDR:

For this question there are (2) kinds of Observables - finite value and infinite value.

http Observables produce finite (1) values and something like a DOM event listener Observables produce infinite values.

If you manually call subscribe (not using async pipe), then unsubscribe from infinite Observables.

Don't worry about finite ones, RxJs will take care of them.

Source 1

I tracked down an answer from Rob Wormald in Angular's Gitter here.

He states (i reorganized for clarity and emphasis is mine)

if its a single-value-sequence (like an http request) the manual cleanup is unnecessary (assuming you subscribe in the controller manually)

i should say "if its a sequence that completes" (of which single value sequences, a la http, are one)

if its an infinite sequence, you should unsubscribe which the async pipe does for you

Also he mentions in this youtube video on Observables that they clean up after themselves... in the context of Observables that complete (like Promises, which always complete because they are always producing 1 value and ending - we never worried about unsubscribing from Promises to make sure they clean up xhr event listeners, right?).

Source 2

Also in the Rangle guide to Angular 2 it reads

In most cases we will not need to explicitly call the unsubscribe method unless we want to cancel early or our Observable has a longer lifespan than our subscription. The default behavior of Observable operators is to dispose of the subscription as soon as .complete() or .error() messages are published. Keep in mind that RxJS was designed to be used in a "fire and forget" fashion most of the time.

When does the phrase our Observable has a longer lifespan than our subscription apply?

It applies when a subscription is created inside a component which is destroyed before (or not 'long' before) the Observable completes.

I read this as meaning if we subscribe to an http request or an observable that emits 10 values and our component is destroyed before that http request returns or the 10 values have been emitted, we are still ok!

When the request does return or the 10th value is finally emitted the Observable will complete and all resources will be cleaned up.

Source 3

If we look at this example from the same Rangle guide we can see that the Subscription to route.params does require an unsubscribe() because we don't know when those params will stop changing (emitting new values).

The component could be destroyed by navigating away in which case the route params will likely still be changing (they could technically change until the app ends) and the resources allocated in subscription would still be allocated because there hasn't been a completion.

upvote
  flag
When a route is navigated away from, then the child router for that route is destroyed. I guess this is why it's not necessary to unsubscribe from router events. – Günter Zöchbauer
upvote
  flag
What about local subjects, which are created by a component (e.g. for in-component logic/wiring up): Should complete() be called on these subjects in ngOnDestroy? That would cleanup the subscriptions and each subscription would have the possibility to clean up, what ever it used, in its complete-handler, right? – Lars
upvote
  flag
@Lars I believe local subjects get cleaned up automatically since they are created within the scope of the parent component but the Angular team is going to be recommending the approach I have detailed above in "Edit 3". – seangwright
5 upvote
  flag
Calling complete() by itself doesn't appear to clean up the subscriptions. However calling next() and then complete() does, I believe takeUntil() only stops when a value is produced, not when the sequence is ended. – Firefly
2 upvote
  flag
@seangwright A quick test with a member of type Subject inside a component and toggling it with ngIf to trigger ngOnInit and ngOnDestroy shows, that the subject and its subscriptions will never complete or get disposed (hooked up a finally-operator to the subscription). I must call Subject.complete() in ngOnDestroy, so the subscriptions can clean up after themselves. – Lars
1 upvote
  flag
@Firefly You are correct - added this to my answer above. @Lars Thanks for for doing the test. I thought local Subjects might be in the set of observables that Angular will clean up for you, if that's not the case then the above solution should be used. – seangwright
2 upvote
  flag
Your --- Edit 3 is very insightful, thanks! I just have a followup question: if using the takeUnitl approach, we never have to manually unsubscribe from any observables? Is that the case? Furthermore, why do we need to call next() in the ngOnDestroy, why not just call complete()? – uglycode
2 upvote
  flag
@uglycode With this approach you never have to unsubscribe unless you want further custom control of your subscriptions. Look at @Firefly's comment above. Calling complete() does not trigger takeUntil(). But it does clean up the ngUnsubscribe Subject. So next() cleans up all the others and complete() cleans up itself. – seangwright
upvote
  flag
@seangwright The documentation for takeUntil says that it listens for the observable to either emit a value or a complete notification, so it seems like just calling complete on the subject is enough. – spongessuck
upvote
  flag
@spongessuck The docs do seem to be contradictory, but looking at the RxJS 5 docs Returns the values from the source observable sequence until the other observable sequence or Promise produces a value. Looking at the source when the takeUntil Observer calls next the source completes. @firefly mentions above that calling complete() alone doesn't seem to do the trick. – seangwright
1 upvote
  flag
@seangwright That's disappointing; the additional boilerplate is annoying. – spongessuck
upvote
  flag
@spongessuck There is a decorator you can use to handle some of the boilerplate github.com/NetanelBasal/angular2-take-until-destroy. I prefer the more explicit approach, but it's a preference. – seangwright
upvote
  flag
first nice post,... my question is if you have a source for your newest edit (EDIT 3), if so could you append it to your answer, thx in advance – Nickolaus
upvote
  flag
@Nickolaus Thanks! I've tried to collect as much information here as possible. My source was a conversation with Ward Bell at NGConf this year. The official sources have not yet been published in the Angular.io docs (as shown by this thread). Unfortunately, this is the best I can do at the moment. You could tweet at ward and ask him about the status on the docs. – seangwright
upvote
  flag
what if I'm subscribing to valueChanges of a FormControl that is a part of a given component? Do I still need to unsubscribe? – Dmitry
1 upvote
  flag
@Dmitry It depends. If that subscription is the valueChanges Observable combined with other observables that outlive the lifetime of the component, then yes, you should use the above pattern. But if you are only subscribing to the valueChanges Observable then it will be destroyed/cleaned up when the component (and the form) are destroyed. – seangwright
2 upvote
  flag
Edit 3 discussed in context of events at medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 – HankCa
upvote
  flag
If following the above pattern what would I spy on to unit test 'ngOnDestroy'? Does this same pattern apply if I'm using ReplaySubject to create an observable from an array that changes? The array is in a service that's meant to live throughout the app's existence. The subscriber for the most part will also have the same lifetime, but in some instances could be destroyed and later reinitialized. – Jens Bodal
1 upvote
  flag
@seangwright, thanks for staying on top of this answer! I have a question that I am still not entirely clear on. If I create a new Subject, should we also be unsubscribing from these too? I haven't been able to find any answers or blog posts that has cleared this up for me. – bmd
1 upvote
  flag
@bmd Take a look at @Lars's comment where it is mentioned that local Subjects do not complete when a component is destroyed unless .complete() is called on them. This is an example of where having an ngUnsubscribe: Subject<void> would help manage all other subscriptions (whether from Observables provided by DOM events, injectable services or other local Subjects). – seangwright
upvote
  flag
Ok, this is very interesting. So did I understand that correctly: I should NOT call this.subscription.unsubscribe() on EVERY subscription, but just need to implement the .takeUntil() method to every Observable and call a .complete() in the ngOnDestroy() method only once? Can you please confirm? – dave0688
upvote
  flag
@dave0688 that is correct. You can keep track of and call .unsubscribe() on every subscription (via for ... of), but using .takeUntil(sub) or .pipe(takeUntil(sub)) is the more rxjs-ish approach – seangwright
upvote
  flag
@seangwright - I am now trying this solution (edit 3). If I console.log(this.ngUnsubscribe) before the 'this.ngUnsubscribe.next()' I see that the observers property of it is an array with 0 items. does it make sense? – Batsheva
upvote
  flag
@seangwright I'm a bit confused as to when to use the lettable operator version of takeUntil. In my component, I have subscriptions to observables returned from a service which makes httpclient requests. In those requests, I use the pipe (tap, catchError, etc). Is that when that version should be used? – Alex
upvote
  flag
@seangwright. Well After a bit more research, I learned that HTTP requests are finite observables and therefore don't have to be unsubscribed. However, I'd still like to see from fleshed out examples of the two forms of takeUntil. – Alex
upvote
  flag
@Alex That is correct, HTTP requests, as explained in my answer above, are finite. But that doesn't mean that an observable that starts out as finite will always result in a finite stream. The RxJs operators allow you to manipulate the stream or data so an initial http request could become an infinite stream of numbers or simulated mouse clicks. This is why the above pattern is recommended. No matter what new operators you add to your http observable you are guaranteed it will be cleaned up. – seangwright
upvote
  flag
@Alex Lettable operators are a bundle/syntax change, not a functional change. You would use them when using a new enough version of RxJs (^5.5.0) to help ensure you import only what is needed per ES module (ie per file). blog.angularindepth.com/… – seangwright
upvote
  flag
Why isn't this "official solution" part of the framework yet? – thorn
1 upvote
  flag
@thorn Good question. It might be that the Angular team wants us to default to letting subscriptions manage themselves using features of the framework (async pipe). RxJs allows you do go a long way towards not having to call unsubscribe() when used with async pipe by combining and manipulating Observables within your component. You can also often async pipe a value into a dumb component's @Input() and work with the raw value from that point forward. I find the ngUnsubscribe() solution helpful when Observables get very complex but I don't know if it should be the default solution. – seangwright

You don't need to have bunch of subscriptions and unsubscribe manually. Use RxJS.Subject and takeUntil combo to handle subscriptions like a boss:

import {Subject} from "rxjs/Subject";

@Component(
    {
        moduleId: __moduleName,
        selector: 'my-view',
        templateUrl: '../views/view-route.view.html',
    }
)
export class ViewRouteComponent implements OnDestroy
{
    componentDestroyed$: Subject<boolean> = new Subject();

    constructor(protected titleService: TitleService)
    {
        this.titleService.emitter1$
            .takeUntil(this.componentDestroyed$)
            .subscribe(
            (data: any) =>
            {
                // ... do something 1
            }
        );

        this.titleService.emitter2$
            .takeUntil(this.componentDestroyed$)
            .subscribe(
            (data: any) =>
            {
                // ... do something 2
            }
        );

        // ...

        this.titleService.emitterN$
            .takeUntil(this.componentDestroyed$)
            .subscribe(
            (data: any) =>
            {
                // ... do something N
            }
        );
    }

    ngOnDestroy()
    {
        this.componentDestroyed$.next(true);
        this.componentDestroyed$.complete();
    }
}

Alternative approach, which was proposed by @acumartini in comments, uses takeWhile instead of takeUntil. You may prefer it, but mind that this way your Observable execution will not be cancelled on ngDestroy of your component (e.g. when you make time consuming calculations or wait for data from server). Method, which is based on takeUntil, doesn't have this drawback and leads to immediate cancellation of request. Thanks to @AlexChe for detailed explanation in comments.

So here is the code:

@Component(
    {
        moduleId: __moduleName,
        selector: 'my-view',
        templateUrl: '../views/view-route.view.html',
    }
)
export class ViewRouteComponent implements OnDestroy
{
    alive: boolean = true;

    constructor(protected titleService: TitleService)
    {
        this.titleService.emitter1$
            .takeWhile(() => this.alive)
            .subscribe(
            (data: any) =>
            {
                // ... do something 1
            }
        );

        this.titleService.emitter2$
            .takeWhile(() => this.alive)
            .subscribe(
            (data: any) =>
            {
                // ... do something 2
            }
        );

        // ...

        this.titleService.emitterN$
            .takeWhile(() => this.alive)
            .subscribe(
            (data: any) =>
            {
                // ... do something N
            }
        );
    }

    // Probably, this.alive = false MAY not be required here, because
    // if this.alive === undefined, takeWhile will stop. I
    // will check it as soon, as I have time.
    ngOnDestroy()
    {
        this.alive = false;
    }
}
upvote
  flag
Why do you use a subject just to keep a bool , while you can use a bool.? – Royi Namir
1 upvote
  flag
If he just use a bool to keep the state, how to make "takeUntil" works as expected? – Val
upvote
  flag
@Val, you are absolutely correct. @Royi, check takeUntil documentation to see explanation how it works xgrommx.github.io/rx-book/content/observable/…. As parameter it accepts only Observable | Promise. – metamaker
upvote
  flag
@acumartini thx for comment! yep, pretty workable solution as well, but it is more about style preference :) you still need to provide each time predicate function to takeWhile and keep track of boolean variable state, which has to be defined somewhere in your component. Same complexity order of solution, though memory usage may be smaller by several bytes (bool var is smaller than Subject<> object). Or did you have something else in your mind? – metamaker
upvote
  flag
Nope, your totally right, style preference :) It simplifies the onDestroy step to one line, which is why I prefer it, but both are good! – acumartini
2 upvote
  flag
I think there is a significant difference between using takeUntil and takeWhile. The former unsubscribes from the source observable immediately when fired, while the latter unsubscribes only as soon as next value is produced by the source observable. If producing a value by the source observable is a resource consuming operation, choosing between the two may go beyond style preference. See the plunk – Alex Che
upvote
  flag
@AlexChe thanks for providing interesting plunk! This is very valid point for general usage of takeUntil vs takeWhile, however, not for our specific case. When we need to unsubscribe listeners on component destruction, we are just checking boolean value like () => alive in takeWhile, so any time/memory consuming operations are not used and difference is pretty much about styling (ofc, for this specific case). – metamaker
upvote
  flag
@metamaker Say, in our component we subscribe to an Observable, which internally mines some crypto-currency and fires a next event for an every mined coin, and mining one such coin takes a day. With takeUntil we will unsubscribe from the source mining Observable immediately once ngOnDestroy is called during our component destruction. Thus the mining Observable function is able to cancel it's operation immediately during this process. – Alex Che
upvote
  flag
OTOH, if we use takeWhile, in the ngOnDestory we just set the boolean variable. But the mining Observable function might still work for up to one day, and only then during it's next call will it realize that there are no subscriptions active and it needs to cancel. – Alex Che
upvote
  flag
During this day unneeded work is done during the mining function, and also your destroyed component is not eligible for garbage collection. – Alex Che

I tried seangwright's solution (Edit 3)

That is not working for Observable that created by timer or interval.

However, i got it working by using another approach:

import { Component, OnDestroy, OnInit } from '@angular/core';
import 'rxjs/add/operator/takeUntil';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/Rx';

import { MyThingService } from '../my-thing.service';

@Component({
   selector: 'my-thing',
   templateUrl: './my-thing.component.html'
})
export class MyThingComponent implements OnDestroy, OnInit {
   private subscriptions: Array<Subscription> = [];

  constructor(
     private myThingService: MyThingService,
   ) { }

  ngOnInit() {
    const newSubs = this.myThingService.getThings()
        .subscribe(things => console.log(things));
    this.subscriptions.push(newSubs);
  }

  ngOnDestroy() {
    for (const subs of this.subscriptions) {
      subs.unsubscribe();
   }
 }
}

Based on : Using Class inheritance to hook to Angular 2 component lifecycle

Another generic approach:

import {Subject} from 'rxjs/Subject';
import {OnDestroy} from '@angular/core';

export abstract class UnsubscribeOnDestroy implements OnDestroy {

    protected componentDestroyed$: Subject<void>;

    constructor() {
        this.componentDestroyed$ = new Subject<void>();

        let f = this.ngOnDestroy;
        this.ngOnDestroy = () => {
            f();
            this.componentDestroyed$.complete();
        };
    }

    ngOnDestroy() {
        // no-op
    }
}

And use :

import { Component, OnInit } from "@angular/core";

@Component({
    selector: 'my-comp',
    template: ``
})
export class RsvpFormSaveComponent extends UnsubscribeOnDestroy implements OnInit {

    constructor() {
        super();
    }

    ngOnInit(): void {
      Observable.of('bla')
      .takeUntil(this.componentDestroyed$)
      .subscribe(val => console.log(val));
    }
}

Since seangwright's solution (Edit 3) appears to be very useful, I also found it a pain to pack this feature into base component, and hint other project teammates to remember to call super() on ngOnDestroy to activate this feature.

This answer provide a way to set free from super call, and make "componentDestroyed$" a core of base component.

class BaseClass {
    protected componentDestroyed$: Subject<void> = new Subject<void>();
    constructor() {

        /// wrap the ngOnDestroy to be an Observable. and set free from calling super() on ngOnDestroy.
        let _$ = this.ngOnDestroy;
        this.ngOnDestroy = () => {
            this.componentDestroyed$.next();
            this.componentDestroyed$.complete();
            _$();
        }
    }

    /// placeholder of ngOnDestroy. no need to do super() call of extended class.
    ngOnDestroy() {}
}

And then you can use this feature freely for example:

@Component({
    selector: 'my-thing',
    templateUrl: './my-thing.component.html'
})
export class MyThingComponent extends BaseClass implements OnInit, OnDestroy {
    constructor(
        private myThingService: MyThingService,
    ) { super(); }

    ngOnInit() {
        this.myThingService.getThings()
            .takeUntil(this.componentDestroyed$)
            .subscribe(things => console.log(things));
    }

    /// optional. not a requirement to implement OnDestroy
    ngOnDestroy() {
        console.log('everything works as intended with or without super call');
    }

}

I like the last two answers, but I experienced an issue if the the subclass referenced "this" in ngOnDestroy.

I modified it to be this, and it looks like it resolved that issue.

export abstract class BaseComponent implements OnDestroy {
    protected componentDestroyed$: Subject<boolean>;
    constructor() {
        this.componentDestroyed$ = new Subject<boolean>();
        let f = this.ngOnDestroy;
        this.ngOnDestroy = function()  {
            // without this I was getting an error if the subclass had
            // this.blah() in ngOnDestroy
            f.bind(this)();
            this.componentDestroyed$.next(true);
            this.componentDestroyed$.complete();
        };
    }
    /// placeholder of ngOnDestroy. no need to do super() call of extended class.
    ngOnDestroy() {}
}
upvote
  flag
you need to use the arrow function in order to bind the 'this': this.ngOnDestroy = () => { f.bind(this)(); this.componentDestroyed$.complete(); }; – Damsorian

The Subscription class has an interesting property:

Represents a disposable resource, such as the execution of an Observable. A Subscription has one important method, unsubscribe, that takes no argument and just disposes the resource held by the subscription.
Additionally, subscriptions may be grouped together through the add() method, which will attach a child Subscription to the current Subscription. When a Subscription is unsubscribed, all its children (and its grandchildren) will be unsubscribed as well.

You can create an aggregate Subscription object that groups all your subscriptions. You do this by creating an empty Subscription and adding subscriptions to it using its add() method. When your component is destroyed, you only need to unsubscribe the aggregate subscription.

@Component({ ... })
export class SmartComponent implements OnInit, OnDestroy {
  private subscriptions = new Subscription();

  constructor(private heroService: HeroService) {
  }

  ngOnInit() {
    this.subscriptions.add(this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes));
    this.subscriptions.add(/* another subscription */);
    this.subscriptions.add(/* and another subscription */);
    this.subscriptions.add(/* and so on */);
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }
}
upvote
  flag
I'm using this approach. Wondering if this is better than using the approach with takeUntil(), like in the accepted answer.. drawbacks ? – Manuel Di Iorio
upvote
  flag
No drawbacks that I'm aware of. I don't think this is better, just different. – Steven Liekens
upvote
  flag
This looks so much cleaner. – Ben

The official Edit #3 answer (and variations) works well, but the thing that gets me is the 'muddying' of the business logic around the observable subscription.

Here's another approach using wrappers.

Warining: experimental code

File subscribeAndGuard.ts is used to create a new Observable extension to wrap .subscribe() and within it to wrap ngOnDestroy().
Usage is the same as .subscribe(), except for an additional first parameter referencing the component.

import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';

const subscribeAndGuard = function(component, fnData, fnError = null, fnComplete = null) {

  // Define the subscription
  const sub: Subscription = this.subscribe(fnData, fnError, fnComplete);

  // Wrap component's onDestroy
  if (!component.ngOnDestroy) {
    throw new Error('To use subscribeAndGuard, the component must implement ngOnDestroy');
  }
  const saved_OnDestroy = component.ngOnDestroy;
  component.ngOnDestroy = () => {
    console.log('subscribeAndGuard.onDestroy');
    sub.unsubscribe();
    // Note: need to put original back in place
    // otherwise 'this' is undefined in component.ngOnDestroy
    component.ngOnDestroy = saved_OnDestroy;
    component.ngOnDestroy();

  };

  return sub;
};

// Create an Observable extension
Observable.prototype.subscribeAndGuard = subscribeAndGuard;

// Ref: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
declare module 'rxjs/Observable' {
  interface Observable<T> {
    subscribeAndGuard: typeof subscribeAndGuard;
  }
}

Here is a component with two subscriptions, one with the wrapper and one without. The only caveat is it must implement OnDestroy (with empty body if desired), otherwise Angular does not know to call the wrapped version.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import './subscribeAndGuard';

@Component({
  selector: 'app-subscribing',
  template: '<h3>Subscribing component is active</h3>',
})
export class SubscribingComponent implements OnInit, OnDestroy {

  ngOnInit() {

    // This subscription will be terminated after onDestroy
    Observable.interval(1000)
      .subscribeAndGuard(this,
        (data) => { console.log('Guarded:', data); },
        (error) => { },
        (/*completed*/) => { }
      );

    // This subscription will continue after onDestroy
    Observable.interval(1000)
      .subscribe(
        (data) => { console.log('Unguarded:', data); },
        (error) => { },
        (/*completed*/) => { }
      );
  }

  ngOnDestroy() {
    console.log('SubscribingComponent.OnDestroy');
  }
}

A demo plunker is here

An additional note: Re Edit 3 - The 'Official' Solution, this can be simplified by using takeWhile() instead of takeUntil() before subscriptions, and a simple boolean rather than another Observable in ngOnDestroy.

@Component({...})
export class SubscribingComponent implements OnInit, OnDestroy {

  iAmAlive = true;
  ngOnInit() {

    Observable.interval(1000)
      .takeWhile(() => { return this.iAmAlive; })
      .subscribe((data) => { console.log(data); });
  }

  ngOnDestroy() {
    this.iAmAlive = false;
  }
}

You usually need to unsubscribe when the components get destroyed, but Angular is going to handle it more and more as we go, for example in new minor version of Angular4, they have this section for routing unsubscribe:

Do you need to unsubscribe?

As described in the ActivatedRoute: the one-stop-shop for route information section of the Routing & Navigation page, the Router manages the observables it provides and localizes the subscriptions. The subscriptions are cleaned up when the component is destroyed, protecting against memory leaks, so you don't need to unsubscribe from the route paramMap Observable.

Also the example below is a good example from Angular to create a component and destroy it after, look at how component implements OnDestroy, if you need onInit, you also can implements it in your component, like implements OnInit, OnDestroy

import { Component, Input, OnDestroy } from '@angular/core';  
import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs/Subscription';

@Component({
  selector: 'my-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()"
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})

export class AstronautComponent implements OnDestroy {
  @Input() astronaut: string;
  mission = '<no mission announced>';
  confirmed = false;
  announced = false;
  subscription: Subscription;

  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
  }

  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }

  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }
}
upvote
  flag
Confused. What are you saying here? You(Angular recent docs/notes) seem to say that Angular takes care of it and then later to confirm that unsubscribe is a good pattern. Thanks. – jamie

Not the answer you're looking for? Browse other questions tagged or ask your own question.