endlacer
endlacer

Reputation: 175

Does a custom complete method of a subscription unsubscribe on its own?

I create an Observable:

public getData(): Observable<any> {
    return this.getEndpoint()
      .pipe(
        switchMap((endpoint) =>
          this.http.get<any>(endpoint)
        ),
        take(1)
      );
  }

I subscribe to the observable and the HTTP call is executed

this.getData().subscribe({
      next: data => this.data = data,
      complete: () => {
        this.someActions();
      },
    });

Due to take(1) in the Observable pipe, the observable completes after one emitted value. So the complete method is called where we do someActions(). Since this method is custom, does the subscription unsubscribe on its own, or do I have to do it manually in the complete method?

Edit: How do I unsubscribe in the complete method of the subscription?

Upvotes: 1

Views: 567

Answers (1)

Mrk Sef
Mrk Sef

Reputation: 8022

You never need to unsubscribe from a completed observable. Observables complete either with a complete() or an error() call to their observer(s). That's part of the contract of an observable.

Consider the following example.

const sub = interval(250).pipe(
  take(4),
  map(x => x + 1)
).subscribe({
  next: console.log,
  complete: () => console.log("This observable completed naturally"),
  err: _ => console.log("This observable completed with an error")
});

console.log("Sub is closed, no need to unsubscribe? " + sub.closed);

setTimeout(() => {
    console.log("Sub is closed, no need to unsubscribe? " + sub.closed);
}, 1250);

console output:

Sub is closed, no need to unsubscribe? false
1
2
3
4
This observable completed naturally
Sub is closed, no need to unsubscribe? true

This demonstrates that observables that will complete do not need to be unsubscribed and do not create memory leaks. The issue is with observables that are long-lived (like an interval that doesn't have a take, takeWhile, takeWhen, ect operator).

In angular, for example, some observables need to be around for as long as the component is there, but no longer. They're linked to a components life-cycle. The issue is that forgetting to unsubscribe to observables like these (ones that do not complete on their own) will cause a memory leak.


Synchronous Example:

of("Hello There").subscribe({
  next: console.log,
  complete: () => console.log("This observable completed naturally"),
  err: _ => console.log("This observable completed with an error")
});

You do not need to unsubscribe to this observable. In fact, because it runs synchronously, it will be closed before it is possible for you to unsubscribe.

 const sub = of(1).subscribe();
 console.log(sub.closed); // output: true

Unsubscribe required example:

const sub = interval(1000).subscribe(console.log);
setTimeout(() => {
    console.log("Sub is closed: " + sub.closed);
    sub.unsubscribe();
    console.log("Sub is closed: " + sub.closed);
}, 60 x 60 x 1000);

Here we start an observable, then we wait an hour to see if it's closed. Then we unsubscribe and check if it's closed. This interval with run forever until it is explicitly unsubscribed.

console output:

0
1
2
... [skipping ahead]
3596
3597
3598
3599
Sub is closed: false
Sub is closed: true

Upvotes: 2

Related Questions