Daniel Bailey
Daniel Bailey

Reputation: 989

How to properly unsubscribe from an Observable Angular 7?

I am currently looking at making sure my application has no memory leaks. I am following this tutorial where he shows how to change from using subscribe() to async as well as showing how to explicitly unsubscribe during ngOnDestroy.

I am struggling to test whether my implementation worked and I wanted to know if my implementation of it in my project is actually unsubscribing.

Here is what is in the tutorial in his component:

@Component({
  /* ... */
  template: `
    <ul *ngIf="todos.length > 0">
      <li *ngFor="let todo of todos">{{todo.name}}</li>
    </ul>   
  `
})
export class TodosComponent implements OnInit, OnDestroy {
  private unsubscribe$ = new Subject<void>();

  todos: Todo[];

  constructor(private store: Store<State>) {}

  ngOnInit() {
    this.store
      .pipe(select(selectTodos), takeUntil(this.unsubscribe$)) // unsubscribe to prevent memory leak
      .subscribe(todos => this.todos = todos);            // unwrap observable
  }

  ngOnDestroy(): void {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}

My ngOnDestroy looks exactly the same as his, but this is what my call to the service looks like:

this.insightsService.getLatestsInsights(5)
      .pipe(takeUntil(this.unsubscribe$))
      .subscribe(z => {
        if (z != null) {
          this.latestInsights = z;
          this.visibleSlides = z.slice(0, this.amountOfSlidesToShow);
        }
      })

So the service works but I just want to know if my implementation is correct? Wasn't sure what the select function was as well or if that is only applicable with what he is using. Let me know if I can provide info.

Upvotes: 1

Views: 14566

Answers (2)

Gh111
Gh111

Reputation: 1452

It's interesting but I can't find any complete answer for this question, I found three approaches, but I'm not sure are they absolutely correct or not, so if anybody knows better solution please provide it. !!! IMPORTANT SYNTAX OF CODE BELOW IS NOT CHECKED

  1. First solution is assigning subscription to a variable and then use it for unsubscribing.
public mySubscription: Observable<Array<Something>>;

export class someComponent OnInit, OnDestroy {


  constructor(private myService: MyService) {}

  ngOnInit() {

      this.mySubscription = this.myService.someMethod().subscribe(response => ... do something);
  }

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

.....
  1. Second solution is using RxJs's Subscription class. All subscriptions can be put together, so that a call to an unsubscribe() of one Subscription may unsubscribe multiple Subscriptions. We can achieve this behavior by "adding" one subscription into another:
....

import { Subscription } from 'rxjs/Subscription';

....

private subscription = new Subscription();

public mySubscription1: Observable<Array<Something>>;
public mySubscription2: Observable<Array<Something>>;

export class someComponent OnInit, OnDestroy {


    constructor(private myService: MyService) { }

    ngOnInit() {

        this.mySubscription1 = this.myService.someMethod().subscribe(response => ... do something);
        this.subscription.add(this.subscription.add(this.mySubscription1));

        this.mySubscription2 = this.myService.someMethod().subscribe(response => ... do something);
        this.subscription.add(this.subscription.add(this.mySubscription1));
    }

    ngOnDestroy() {

        if (this.subscription) {
            this.subscription.unsubscribe();
        }
    }

.....
  1. Third and final solution is using rxjs's takeUntil operator
....

import { Subject } from 'rxjs';

....

private unsubscribe = new Subject();

export class someComponent OnInit, OnDestroy {

    constructor(private myService: MyService) { }

    ngOnInit() {

        this.myService.someMethod()
            .pipe(takeUntil(this.unsubscribe))
            .subscribe(response => ... do something);

    }

    ngOnDestroy() {

        this.unsubscribe.next();
        this.unsubscribe.complete();
    }

.....

Upvotes: 7

SiddAjmera
SiddAjmera

Reputation: 39432

In the tutorial, he's using an Rgrx Store. This store can yield values on multiple occasions. So it's not really a one-time thing. And for the same reason, unsubscribing from the Observable is necessary.

In your example, you're calling the insightsService.getLatestsInsights(5) which is returning an Observable. But I think this would just be a one-time thing as in your getLatestsInsights, most probably, you're returning an Observable that is returned by one of the methods from HttpClient. And these methods generally return Observables that just yield a value once and get completed. I'm just assuming that here. Please do correct me if I'm wrong.

So if that really is a case, then you don't really need to unsubscribe from your Subscription in the first place, let alone think about doing it correctly.

You can read more about when you should unsubscribe on the article RxJS: Don’t Unsubscribe by Ben Lesh who's an Rxjs Lead.

Upvotes: 1

Related Questions