Search code examples
typescriptrxjses6-promise

lastValueFrom does not work while toPromise works


While migrating from toPromise() to firstValueFrom/lastValueFrom due to the deprecation, I've run into an issue. Neither firstValueFrom/lastValueFrom works while toPromise() works as expected. The example code below shows my usecase. Neither of the 3 promises resolves on next(), while firstValueFrom should. Only toPromise() resolves on complete, while all 3 should. In the end the firstValueFrom/lastValueFrom produces EmptyError.

const subject = new Subject<boolean>();

setTimeout(async () => {
  await subject.toPromise();
  console.log("subject toPromise finished");
});

setTimeout(async () => {
  await firstValueFrom(subject);
  console.log("subject firstValueFrom finished");
});

setTimeout(async () => {
  await lastValueFrom(subject);
  console.log("subject lastValueFrom finished");
});

subject.next(true);
subject.complete();

Solution

  • The problem was that the firstValueFrom method was called after the Observable was completed. This was not a problem for toPromise() because it won't generate any errors in this case. But firstValueFrom/lastValueFrom generates an error if the Observable is already completed at the time when these are called.

    The solution for me was to change the Subject to ReplaySubject, so when firstValueFrom/lastValueFrom is called the next() and complete() calls will be replayed.

    Subjective opinion: I think this behavior is confusing. Generating a Promise from a source that is already finished should result in the same behavior as Promise.resolve(). Also if firstValueFrom/lastValueFrom is called between next() and complete() the resulting behavior is not that telling. When using Promises we usually operate with a single value that's either there or not there. So the original behavior of toPromise() where we resolve with undefined is a more logical default behavior.