lastValueFrom will throw EmptyError if the Observable is complete BEFORE lastValueFrom is called
air2 opened this issue · 7 comments
Describe the bug
If I call lastValueFrom
after the complete is called, it will fail
Expected behavior
I expect lastValueFrom
to give me the last value even if the Observable is complete before lastValueFrom
from is called.
Reproduction code
it('test lastValueFrom', async () => {
const ob = new BehaviorSubject<string>('')
ob.next('value')
ob.complete()
const val = await lastValueFrom(ob)
expect(val).toEqual('value') //this will fail
})
it('test lastValueFrom 2', async () => {
const ob = new BehaviorSubject<string>('')
ob.next('value')
const val = lastValueFrom(ob)
ob.complete()
expect(await val).toEqual('value')
})
### Reproduction URL
_No response_
### Version
7.5.7
### Environment
_No response_
### Additional context
_No response_
I don't think this is a bug. Completing a subject essentially closes it, so it will lose whatever value it has, and you won't be able to push new ones anymore:
const subject = new BehaviorSubject(1);
subject.next(2);
subject.complete();
subject.subscribe({
next: v => console.log('value', v),
complete: () => console.log('complete')
});
// Only "complete" is logged
lastValueFrom
can't get the value from an observable that just completes, so throwing EmptyError is correct.
Well I do not agree, the value now depends on the moment you initiate the lastValueFrom call, not on the actual last value of the Observable. But IF this is not considered a bug, it should definitely be documented.
https://rxjs.dev/api/index/function/lastValueFrom
If the observable stream completes before any values were emitted, the returned promise will reject with EmptyError or will resolve with the default value if a default was specified.
It is documented clearly as very first sentence.
Yes, but values ARE emitted, so the documentation is not clear about this situation. In other words, you have to initiate lastValueFrom
BEFORE the observable completes to get its last value. This is not stated anywhere. I would like the lastValueFrom
to return the value of the observable if it is completed and there is a value, if there is not value it should indeed return a promise that rejects with EmptyError
This issue should not be closed, it is not solved, the documentation is not clear IMHO. It only states that the promise rejects when there are NO values emitted. But that is not the case here.
No, values are not emitted. Observable's fundamental contract is it works lazely for the each subscription. If you have an observable, but if there aren't any subscription, there is no actual value emission occurs. So if there is a observable have value then completes without subscription it is essentially does nothing. Once it's completed, subscribing it won't able to give any values for those reason.
We do not describe observable itself's behavior in each operator document. I believe doc for the operator suffeciently describes its behavior.
@air2 in case it helps, I think the name lastValueFrom
is confusing you.... BehaviorSubject has a .getValue()
function that you can call to get the last value emitted on that BehaviourSubject when you call it, maybe it's what you're looking for. (but I'm not sure it would work on a BehaviorSubject you called .complete().... In general you shoudn't call .complete() on a BehaviorSubject unless you want to remove it from memory)
lastValueFrom
on the other hand is an operator for any Observable (not only subjects), that always returns a promise when you call it. The promise will resolve when the Observable is listening to completes, at which point it will resolve it with the last value that observable emitted.
When you call .complete()
on a BehaviorSubject, it loses the value it had before, because you closed it. All it will do from now on is just emit "complete" when anybody subscribes again. So when lastValueFrom
subscribes to it, all it "sees" is that the observable just completes without emitting any value, so it rejects the promise.
The contracts of lastValueFrom
are very clear if you also understand the concept of Observables in general and how they behave:
Converts an observable to a promise by subscribing to the observable, waiting for it to complete, and resolving the returned promise with the last value from the observed stream.
...
If the observable stream completes before any values were emitted, the returned promise will reject with EmptyError or will resolve with the default value if a default was specified.
It's all it's doing, word by word. Subscribes to the observable, the observable completes, it didn't emit anything, it rejects the promise.