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();
Varun Sukheja
  • 6,170
  • 5
  • 51
  • 93
  • Also have a look at https://github.com/NetanelBasal/ngx-take-until-destroy – martin Jun 21 '19 at 11:13
  • 2
    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 ? – Abdulrahman Falyoun Jun 21 '19 at 11:14
  • Possible duplicate of [Angular/RxJs When should I unsubscribe from \`Subscription\`](https://stackoverflow.com/questions/38008334/angular-rxjs-when-should-i-unsubscribe-from-subscription) – David Jun 21 '19 at 12:13

6 Answers6

8

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)

raaaay
  • 496
  • 7
  • 14
Nikhil Kapoor
  • 547
  • 1
  • 4
  • 15
  • @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 unsubscribe – Nikhil Kapoor Jun 21 '19 at 11:33
  • 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 https://gist.github.com/btroncone/fe9d9aaf457f2ebd72c4624e34d664f8 – Junaid Oct 07 '20 at 11:27
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.

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();
  }
}
eddy
  • 4,373
  • 16
  • 60
  • 94
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

ymssa___
  • 993
  • 9
  • 16
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>
Owen Kelvin
  • 14,054
  • 10
  • 41
  • 74
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();
  }

} 
Darren Shewry
  • 10,179
  • 4
  • 50
  • 46