8

From my understanding of Angular and RxJs there are two ways to terminate Observables. You can unsubscribe() from them or use takeUntil() and complete(). Below are examples of each approach (in pseudocode).

The unsubscribe() approach

private _id: number;
private _subscriptions: Subscription[] = [];

constructor(private _route: ActivatedRoute) {
    this._getId();
}

public ngOnDestroy(): void {
    this._subscriptions.forEach(
        subscription => subscription.unsubscribe()
    );
}

private _getId(): void {
    this._subscriptions.push(
        this._route.params.subscribe(params => this._id = +params['id'])
    );
}

The takeUntil() and complete() approach

private _id: number;
private _ngUnsubscribe: Subject<void> = new Subject<void>();

constructor(private _route: ActivatedRoute) {
    this._getId();
}

public ngOnDestroy(): void {
    this._ngUnsubscribe.next();
    this._ngUnsubscribe.complete();
}

private _getId(): void {
    this._route.params.takeUntil(this._ngUnsubscribe).subscribe(
        params => this._id = +params['id']
    );
}

In Angular, is there a preferred way to terminate Observables?

martin
  • 76,615
  • 21
  • 156
  • 193
ebakunin
  • 2,832
  • 6
  • 23
  • 41
  • 2
    The preferred way is in `ngOnDestroy`, which you are doing. The only thing I might add is to check that the values are "truthy", meaning not null or not undefined, otherwise you will get an error trying to call `unsubscribe()`. – Lansana Camara Jul 26 '17 at 17:39
  • 1
    And note that you often don't need to terminate common observables, such as the router parameters in your example. Angular terminates them automatically. See the notes near the bottom of this section of the docs: https://angular.io/guide/router#activatedroute-the-one-stop-shop-for-route-information – DeborahK Jul 26 '17 at 17:43
  • 1
    see [this answer](https://stackoverflow.com/a/41177163/2545680) – Max Koretskyi Jul 26 '17 at 18:06

2 Answers2

7

Both approaches are correct even though they aren't equivalent.

In my opinion (and experience) using unsubscribe() make usually more sense and is more obvious to other developers that don't have extensive experience with RxJS.

Using takeUntil() is recommended by the lead developer of RxJS 5 (https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87) and is sometimes easier to use than handling multiple subscription objects. For example if you use the partition() operator to split one stream into two it's easier to use just takeUntil(...).partition(...).

However there are two important things:

  1. These two aren't the same. If you use takeUntil() you're completing the Observable chain which means that all complete handles are called followed by tear down functions. On the other hand when you call unsubscribe() only tear down functions are called (including operators such as finally()).

    This is why I think it makes more sense to use unsubscribe(). With takeUntil() you might have a complete handler that is invoked even though you just wanted to unsubscribe (not mentioning that this triggers operators that work with the complete signal such as repeat() that might resubscribe again). The fact you want to unsubscribe doesn't mean that the source Observable completed. You just don't care any more about its values so it's probably better to use unsubscribe() in this case.

    However, in practise it usually doesn't matter whether you complete the chain or just unsubscribe.

  2. You can compose Subscriptions into a single one and unsubscribe all of them at once:

    const subscription = new Subscription();
    
    const sub1 = Observable...subscribe(...);
    const sub2 = Observable...subscribe(...);
    const sub3 = Observable...subscribe(...);
    
    subscription.add(sub1).add(sub2).add(sub3);
    
    ...
    
    subscription.unsubscribe(); // unsubscribes all of them
    
martin
  • 76,615
  • 21
  • 156
  • 193
  • Your example works fine. It just was not obvious to me that the short version `subscription = Observable...subscribe(...).add(sub1).add(sub2); subscription.unsubscribe();` unsubscribes only the latest subscription `sub2` because of `subscription.add(...)` method returns its argument. – Evgeniy Generalov Sep 29 '18 at 13:30
0

The way that I do this is by first checking on ngOnDestroy, if the Observable exists. If yes, then unsubscribe from it. Following is the code snippet from my app.

 accountSubscriptionObj : Subscription;

 ngOnDestroy() {
    if (this.accountSubscriptionObj) {
      this.accountSubscriptionObj.unsubscribe();
    }
  }

Meanwhile, you don't need to unsubscribe from Async pipe, @HostListener, Finite Observable. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks. When you have a finite sequence, usually you don’t need to unsubscribe, for example when using the HTTP service or the timer observable. For more reference read here.

monica
  • 1,446
  • 12
  • 25