11
votes

What is the best strategy to unsubscribe multiple observable in angular? Just to be clear I use this approach when the application is not big and do not need solutions such as ngrx that in this case would be an overengineering.

Currently I use a subscription instance to add all subscriptions and after that when the component was destroyed i call the unsubscribe method. But also I have seen alternatives using the takeUntil from rxjs.

export class MyComponent implements OnInit, OnDestroy {

  $firstObservable: Observable<number> = timer(0, 1000);
  $secondObservable: Observable<number> = timer(0, 1000);

  private _subscriptions = new Subscription();

  constructor() { }

  ngOnDestroy(): void {
    this._subscriptions .unsubscribe();
  }

  ngOnInit(): void {
    this._subscriptions .add(
      this.$firstObservable.subscribe(console.log));

    this._subscriptions .add(
      this.$secondObservable.subscribe(console.log));
  }

}

What would be the best solution?

3
You can merge all those subscriptions to one .subscribe – Josef Katič
takeUntil is currently considered to be the best practice. – kos
Using takeUntil is the easiest way you can do. Btw, you can chain this._subscriptions.add().add().add() calls which makes it a little shorter. – martin

3 Answers

9
votes

I suggest you to use takeUntil() pipeable operator: https://www.learnrxjs.io/operators/filtering/takeuntil.html

This way, you create a Subject which is to emit value on ngOnDestroy and unsubscribe multiple subscriptions all at once

 unsubscribeSignal: Subject<void> = new Subject();

 $firstObservable: Observable<number> = timer(0, 1000);
 $secondObservable: Observable<number> = timer(0, 1000);

 ngOnInit() {

    this.$firstObservable
    .pipe(
       takeUntil(this.unsubscribeSignal.asObservable()),
    )
    .subscribe(result => {});

    this.$secondObservable
    .pipe(
       takeUntil(this.unsubscribeSignal.asObservable()),
    )
    .subscribe(result => {});

  }

  ngOnDestroy(){
    this.unsubscribeSignal.next();
    // Don't forget to unsubscribe from subject itself
    this.unsubscribeSignal.unsubscribe();
  }
5
votes

I'm very disappointed that people don't even mention the async pipe.

It's very powerful, allowing you to both stop worrying about subscriptions and using on push detection strategy.

To use it, simply remove the subscriptions from your TS, and assign the values to your variables which will be typed as observables.

Just look at the code reduction and simplessness (don't know if it's a word, but kinda don't care)

export class MyComponent {
  firstObservable$: Observable<number> = timer(0, 1000);
  secondObservable$: Observable<number> = timer(0, 1000);

  combination$ = combineLatest(this.firstObservable$, this.secondObservable$)
    .pipe(tap(() => console.log()));
}

In your HTML

<ng-container *ngIf="combination$ | async">
  Observables are being observed.
</ng-container>

(Even though it's not suited to your question example, you can already see how it's way cleaner & simpler)

And best part, you don't have to worry about memory leaks anymore. Angular takes care of all the subscriptions, leaving you to only care about your code (which is what a good framework is supposed to do).

3
votes

simplest way by store the subscribtion object and on ngDestory call unsubscribe method.

export class MyComponent implements OnInit, OnDestroy {

  $firstObservable: Observable<number> = timer(0, 1000);
  $secondObservable: Observable<number> = timer(0, 1000);
   
  private _subscriptions :any[] = [];

  constructor() { }

  ngOnDestroy(): void {
    this._subscriptions .unsubscribe();
  }

  ngOnInit(): void {
    this._subscriptions .push(
      this.$firstObservable.subscribe(console.log)
    );

    this._subscriptions .push(
      this.$secondObservable.subscribe(console.log)
    );
  }

   ngOnDestroy(){
    this._subscriptions.forEach(sub => sub.unsubscribe()) ; // πŸ§™β€β™‚οΈπŸ§™β€β™‚οΈ
  }

}