18
votes

I've been looking around for a simple way to avoid the memory leaks I've read about caused by failing to unsubscribe. Most of the time I'm just wanting the ONE response from my backend. And then I'll want to unsubscribe. So why not call it in the callback?

  onSubmit(){
    var subscription = this.puzzleService.login(this.nameoremail, this.password).subscribe( success =>{
      if(success){
        this.router.navigate(['/puzzles']);
      }
      else{
        this.message="Login failed. Please try again.";
      }
      this.loading=false;
      subscription.unsubscribe();
    });
    this.loading=true;
  }

Note the assignment of the subscription to a local variable. This local variable is then locked inside a closure and told to unsubscribe when its work is done. No class variables, no takeUntil, nothing else.

It compiles and runs without error. I'm not familiar enough with the debugger to determine if the observable object is actually destroyed and subsequently garbage collected.

Is there something I'm missing? Can someone more familiar with the debugger correct me? Because if this works I'm going to be doing this everywhere. Except in my pollWords() function...

This seems much simpler than other solutions I've seen advocated. I'd think I didn't even really need the closure, as when I look at it in the debugger I see "_this" is the closure for "this" and "this" is actually the observable. So if there was some way to prevent the munging that is happening to "this" then I could call "this.unsubscribe()" and be done. Not that an object reference closure is a horrible thing...

References:

Angular/RxJs When should I unsubscribe from `Subscription`

Rxjs Observable Lifecycle

4
See this recent GitHub question: github.com/ReactiveX/rxjs/issues/4318cartant

4 Answers

25
votes

A more RxJS-ish way of doing what you want is to make use of the take operator, like this:

this.puzzleService.login(this.nameoremail, this.password).pipe(
  take(1)
).subscribe(success => {
  // ...
});

This will make the observable emit exactly one value and then complete it.

However, @Picci is totally right that if your login method returns a result from a HttpClient request, you don't need to unsubscribe manually (nor use take, for that matter).

8
votes

Unsubscribing is important to avoid memory leaks, but it maybe not so important if you are "just wanting the ONE response from backend".

What I mean is that if, behind your this.puzzleService.login you are using the Angular http service, than you do not have to worry about unsubscribing since the service itself unsubscribes as soon as it gets the response from the backend, or an error occurs.

Unsubcribing is important when you face Observable streams which emit, by their nature, more than one value, and this is not the case of an http call but may be the case of web-sockets streams or other kind of streams.

This article from Ben Lesh, top contributor if not lead of RxJS, casts some light on the topic.

0
votes

A construction that should work and which is also 'legal' I think is this:

onSubmit() {
    const subscription = this.puzzleService.login(this.nameoremail, this.password)
        .subscribe( success => {
            // your code
            // more of your code
            this.loading = false;
            setTimeout(() => subscription.unsubscribe(), 0);
        });
    this.loading = true;
}

Now the .unsubscribe() is executed after the code in .subscribe(...), which means that subscription exists, and can be unsubscribed from.

Where .take(1) will work when the Observable is created in the same component, I think you cannot just end the Observable if it is not yours, e.g. when created in / managed by another component or service, since there might be other subscribers as well. Then you really only want to end your own subscription, not complete the Observable as such.

-1
votes

Its a very hard question - I do also sometimes unsubscribing with local variables but I see this way is not so proper as looks like. YOu can read about all the corner cases (saving references to this, to component variables, etc) in the article of reactive Fox here: https://medium.com/angular-in-depth/why-you-have-to-unsubscribe-from-observable-92502d5639d0