I have not been able to find any normative text to answer this question. I have been using this code pattern (this is in TypeScript in an Angular application):
observeSomethingFun$: Observable<Fun>;
...
async loadsOfFun() {
const fun = await this.observeSomethingFun$.toPromise();
// I now have fun
}
In general, Observables need to be unsubscribed from. This happens automatically when the Angular async
pipe is used but what happens in this case? Does toPromise
unsubscribe after emitting one value?
If not, how do I unsubscribe manually?
Update:
It turns out @Will Taylor's answer below is correct but my question needs some clarification.
In my case the Observable emits a never-ending stream, unlike for example Angular's HttpClient Observables that complete after emitting one value. So
in my case I would never get past the await
statement according to Taylor's answer.
RxJS makes this easy to fix. The correct statement turns out to be:
const fun = await this.observeSomethingFun$.pipe(first()).toPromise();
The RxJS first
operator will receive the first value and unsubscribe from the source Observable. it will then send out that value to the toPromise
operator
and then complete.
The toPromise function lives on the prototype of Observable and is a util method that is used to convert an Observable into a Promise . Inside this function we subscribe to the Observable and resolve the Promise with the last emitted value - attention - when the Observable completes!
If and when an observable completes, any subscription to it will automatically be unsubscribed. In many cases, an observable will never emit a completion event.
An Observable subscription has an unsubscribe() function to releases resources/memory or to cancel the observable execution. In the above example we have multiple subscription(). We have stored them in a subscription() array and we have unsubscribe() them one by one in ngOndestroy() as shown in the above example.
⚠ toPromise is not a pipable operator, as it does not return an observable.
First of all, thank you for this question and answer, I wrongly assumed toPromise()
knew what it was doing in all scenarios and would unsubscribe when that observable completes (even if it is an observable stream)
So I will just say that it doesn't hurt to pipe all of your observables before using .toPromise()
I just went through a big ordeal of stepping through our app for memory leaks and found the above answer by Will to be good. The elaboration on the actual question was exactly the same issue I was running into.
We are stepping through each observable in the app right now and we use either
pipe(take(1))
which is equivalent to pipe(first())
.
or we use pipe(takeUntil(this.destroyed))
where this.destroyed.next(true)
is called when we destroy our particular component or service.
We use take()
to keep our verbiage consistent so we can search for take or takeUntil across various components.
Long story short, yeah you might take a very slight performance hit piping your observables at each instance, but I highly recommend doing so in order to prevent any unwanted app-wide memory leak hunts. Then maybe if you have the time you can step through each one and see where .toPromise()
actually unsubscribes correctly for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With