Angular better way to clear subscriptions
Asked Answered
M

6

7

There are many ways to handle multiple subscriptions efficiently in a component, I have 2 ways here and wanted to know which is more efficient and why??

Method 1: Using Array

Step 1: creating Array

private subscriptionArray: Subscription[];

Step 2: Adding subscriptions to the array

this.subscriptionArray.push(this._storeManagementHttp.createStore(newStore).subscribe(resp => {
  this._toast.success('New store created');
}));

Step 3: iterating each subscription and unsubscribing

this.subscriptionArray.forEach(subs => subs.unsubscribe());

Method 2

Step 1: creating a new subscription

private subscriptions = new Subscription();

Step 2: Adding subscriptions

this.subscriptions.add(this._storeManagementHttp.createStore(newStore).subscribe(resp => {
  this._toast.success('New store created');
  this._router.navigate(['/store-management']);
}));

Step3: Clearing subscription

this.subscriptions.unsubscribe();
Mcginty answered 21/6, 2019 at 11:4 Comment(3)
Also have a look at github.com/NetanelBasal/ngx-take-until-destroyBotulism
Why you call them two different ways when both of them use the same way except that the first approach you are storing them in an array ?Flatulent
Possible duplicate of Angular/RxJs When should I unsubscribe from `Subscription`Sayre
M
9

You can also this one, you don't need to run loop in this case

    private destroy$ = new Subject();
    
    myservice.megohd().pipe(takeUntil(destroy$)).subscribe();
    
    ngOnDestroy() {
      this.destroy$.next();
      this.destroy$.complete();
    }

read here (https://www.learnrxjs.io/operators/filtering/takeuntil.html)

Moscow answered 21/6, 2019 at 11:18 Comment(2)
@BernoulliIT what? complete() will mark the observable complete at ngOnDestroy what else you want ? and there will be no memory leak. the guy asked for the way not how to unsubscribeMoscow
Also, takeUntil() will complete the observable before unsubscribing. So, no need to call this.destroy$.complete(); again, just the this.destroy$.next() will do the trick. Check out some more quick tips here gist.github.com/btroncone/fe9d9aaf457f2ebd72c4624e34d664f8Hydrolysate
K
2

You also have the third option, which is a custom RxJS operator.

I had created one and found out that Netanel Basal had found it too, so I'll give his clean code.

You can install UntilDestroyed or use the code :

function isFunction(value) {
  return typeof value === 'function';
}

export const untilDestroyed = (
  componentInstance,
  destroyMethodName = 'ngOnDestroy'
) => <T>(source: Observable<T>) => {
  const originalDestroy = componentInstance[destroyMethodName];
  if (isFunction(originalDestroy) === false) {
    throw new Error(
      `${
        componentInstance.constructor.name
      } is using untilDestroyed but doesn't implement ${destroyMethodName}`
    );
  }
  if (!componentInstance['__takeUntilDestroy']) {
    componentInstance['__takeUntilDestroy'] = new Subject();

    componentInstance[destroyMethodName] = function() {
      isFunction(originalDestroy) && originalDestroy.apply(this, arguments);
      componentInstance['__takeUntilDestroy'].next(true);
      componentInstance['__takeUntilDestroy'].complete();
    };
  }
  return source.pipe(takeUntil<T>(componentInstance['__takeUntilDestroy']));
};

Then your subscriptions become

this.myService.subject.pipe(untilDestroyed(this)).subscribe(...);

Note that because of AOT compilation, you have to write a ngOnDestroy method, otherwise the operator isn't able to create it from scratch.

Kentledge answered 21/6, 2019 at 11:33 Comment(1)
Does it behave the same way as the answer of @Nikhil Kapoor?Knowles
M
2

There's better, more succinct solution: https://www.npmjs.com/package/subsink by Ward Bell

export class SomeComponent implements OnDestroy {
  private subs = new SubSink();

  ...
  this.subs.sink = observable$.subscribe(...);
  this.subs.sink = observable$.subscribe(...);
  this.subs.sink = observable$.subscribe(...);
  ...

  // Unsubscribe when the component dies
  ngOnDestroy() {
    this.subs.unsubscribe();
  }
}
Malony answered 7/4, 2022 at 17:57 Comment(0)
J
0

I would prefer Method 2.

On Method 1 there is no problem.

It works perfectly fine. The problem with this approach is that we’re mixing observable streams with plain old imperative logic.

Method 2 is a built in mechanism to enhance this approach.

Read here

Jodoin answered 21/6, 2019 at 11:10 Comment(0)
Z
0

You have good answers provided though I would try to avoid all the above methods if possible and go for async 'pipe, that is allow angular do the subscription and unsubscription.

Lets consider a number of Observables say 5 observables

observale1$: Observable<IDataDto1>;
observale2$: Observable<IDataDto2>;
observale3$: Observable<IDataDto3>;
observale4$: Observable<IDataDto4>;
observale5$: Observable<IDataDto5>;

To avoid subscribing to all this we can create one single Observable say v$


import { forkJoin } from 'rxjs';
v$ = forkJoin({
  observale1: observale1$,
  observale2: observale2$,
  observale3: observale3$,
  observale4: observale4$,
  observale5: observale5$
});

With the above we can wrap our component.html in an *ngIf and allow angular to auto subscribe and unsubscribe


<ng-container *ngIf='v$ | async as v'>
  <!-- Your html code here -->
</ng-container>
Zebec answered 13/12, 2020 at 12:28 Comment(0)
D
0

Method 2 because Subscription.unsubscribe() does more than just seemingly unsubscribing, see its source code here.

e.g.


class SomeClass implements OnInit, OnDestroy { {

  // Setup your subscriptions
  private subscriptions = new Subscription();

  // Example Observable service
  constructor(private someService: SomeService) {}

  ngOnInit(): void {
    this.subscriptions.add(this.someService.getStuff());
    // ...
    this.subscriptions.add(/* etc */);
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }

} 
Dulci answered 27/6, 2022 at 15:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.