5

I'm currently wondering about angular subscription and unsubscription. There is a lot of stuff on the subject, so I'm a bit lost in all of this.

When should I unsubscribe from subscription ? What happens if I don't, ever, unsubscribe from them ? I never encounter any errors from reliquat subscription.

Is there a way to auto unsubscribe from everything in a component/app, so that I don't have to declare 1 property by subscription ? This can be very annoying :

@Component({
  selector: 'subscriptionTest',
  template: `...`,
})
export class SubTestComponent {
  first;
  second;
  third;

  constructor(private remote: RemoteService){}

  ngOnInit() {
    this.first = remote.getSomeData().subscribe(data => // do something);
    this.second = Observable.interval(500).subscribe(event => // do something);
    this.third = remote.getSomeOtherData().subscribe(data => // do something);

  }

  ngOnDestroy() {
    this.first.unsubscribe();
    this.second.unsubscribe();
    this.third.unsubscribe();
  }

}
Boulboulouboule
  • 4,087
  • 1
  • 13
  • 29
  • 1
    https://stackoverflow.com/questions/38008334/angular-rxjs-when-should-i-unsubscribe-from-subscription this might be helpful. Also this is another gud article on how and when to unsubscribe. https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 – Fahad Nisar Nov 07 '17 at 08:08

4 Answers4

6

If you don't want to repeat yourself, use a super class:

/**
*  Unsubscribe from any Observable using takeUntil and this.destroy$
*      someObservable.pipe( // lettable operators in rxjs ^5.5.0
*          ...
*          takeUntil(destroy$)
*      )
*      .subscribe(....
**/
export class Destroyer implements OnDestroy {
    destroy$: Subject<boolean> = new Subject<boolean>();

    ngOnDestroy() {
        // Unsubscribe from whatever used takeUntil(destroy$)
        this.destroy$.next(true);
        // Now let's also unsubscribe from the subject itself:
        this.destroy$.unsubscribe();
    }
}

Use that everywhere:

export class SomeComponent extends Destroyer{

   constructor(private remote: RemoteService){
       super();
   }

   ngOnInit() {
        remote.getSomeData()
              .pipe(takeUntil(this.destroy$)) 
              //.takeUntil(this.destroy$) // before rxjs ^5.5.0
              .subscribe(data => {/* do something*/});

        Observable.interval(500)
              .pipe(takeUntil(this.destroy$))
              //.takeUntil(this.destroy$) // before rxjs ^5.5.0
              .subscribe(data => {/* do something*/});
      }
   }

   // optional. You don't need ngOnDestroy everywhere.
   ngOnDestroy(): void {

       // Let Destroyer trigger unsubscribe
       super.ngOnDestroy();

       // ...SomeComponent other cleanup.
   }


}
  • In Angular you don't have multiple inheritance, so this method can be difficult in some implementations of another needs of extends. Like an of many Angular's class for example. – diegodsp Jan 10 '19 at 21:52
5

The takeUntil operator is a simple way to "auto" unsubscribe from any subscription, example :

@Component({...})
export class AppComponent implements OnInit, OnDestroy {
  destroy$: Subject<boolean> = new Subject<boolean>();

  constructor(private apollo: Apollo) {
  }

  ngOnInit() {
    this.apollo.watchQuery({
        query: gql`
        query getAllPosts {
          allPosts {
            title
            description
            publishedAt
          }
        }
      `
      })
      .takeUntil(this.destroy$)
      .subscribe(({data}) => {
        console.log(data);
      });
      remote.getSomeData().subscribe(data => // do something);
      Observable.interval(500).subscribe(event => // do something);
      remote.getSomeOtherData().subscribe(data => // do something);
  }

  onStartInterval() {
    Observable
      .interval(250)
      .takeUntil(this.destroy$)
      .subscribe(val => {
        console.log('Current value:', val);
      });
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    // Now let's also unsubscribe from the subject itself:
    this.destroy$.unsubscribe();
  }
}

You don't have to declare each subscription , only to add the .takeUntil(this.destroy$) operator on each of them

Source : https://alligator.io/angular/takeuntil-rxjs-unsubscribe/

An amazing post about subscriptions : Angular/RxJs When should I unsubscribe from `Subscription`

Boulboulouboule
  • 4,087
  • 1
  • 13
  • 29
  • 1
    The best way is use Subscription class. You can i.e.: Create a variable, like subs: Subscription = Subscription.EMPTY; And then... this.subs.add(this.route.data.subscribe(... or this.subs.add(this.clientService.login(this.username, this.password).subscribe(... And in your ngOnDestroy() make a unique call, like this.subs.unsubscribe(); – diegodsp Jan 10 '19 at 21:41
4

The best way is use Subscription class.

For that, you will create a variable in your component, like this...

private subs: Subscription = Subscription.EMPTY;

And then use like this...

this.subs.add(this.route.data.subscribe(...

or

this.subs.add(this.clientService.login(this.username, this.password).subscribe(...

And in your ngOnDestroy() make a unique call, like

this.subs.unsubscribe();
diegodsp
  • 882
  • 8
  • 12
  • This doesn't work for me. `this.subs = Subscription.EMPTY; this.sub = foo$.subscribe(); this.subs.add(this.sub); console.log(this.sub.closed);`. Then `this.sub.closed` is always `true`, even when I don't call `this.subs.unsubscribe();` – Benjamin M Jun 08 '19 at 21:28
  • This solution works replacing `subs: Subscription = Subscription.EMPTY;` with `private subs = new Subscription();` – Miki Mar 15 '21 at 21:46
1

You can use third party plugins like ngx-auto-unsubscribe

JsFan
  • 393
  • 2
  • 4
  • 13
  • Oh thanks :) ! With this lib I still have to declare each subscription ? And I prefer not using a lib for something I can do with native code –  Nov 07 '17 at 08:26