apps/rxjs.dev/content/deprecations/to-promise.md
The similarity between Observables and Promises is that both collections may produce values over time, but the difference is that Observables may produce none or more than one value, while Promises produce only one value when resolved successfully.
For this reason, in RxJS 7, the return type of the Observable's toPromise()
method has been fixed to better reflect the fact that Observables can yield zero values. This may be a breaking
change to some projects as the return type was changed from Promise<T> to Promise<T | undefined>.
Also, toPromise() method name was never indicating what emitted value a Promise will resolve with because Observables
can produce multiple values over time. When converting to a Promise, you might want to choose which value to pick -
either the first value that has arrived or the last one. To fix all these issues, we decided to deprecate toPromise(),
and to introduce the two new helper functions for conversion to Promises.
As a replacement to the deprecated toPromise() method, you should use one of the two built in static conversion
functions {@link firstValueFrom} or {@link lastValueFrom}.
lastValueFromThe lastValueFrom is almost exactly the same as toPromise() meaning that it will resolve with the last value that has
arrived when the Observable completes, but with the difference in behavior when Observable completes without emitting a
single value. When Observable completes without emitting, toPromise() will successfully resolve with undefined (thus
the return type change), while the lastValueFrom will reject with the {@link EmptyError}. Thus, the return type of the
lastValueFrom is Promise<T>, just like toPromise() had in RxJS 6.
import { interval, take, lastValueFrom } from 'rxjs';
async function execute() {
const source$ = interval(2000).pipe(take(10));
const finalNumber = await lastValueFrom(source$);
console.log(`The final number is ${finalNumber}`);
}
execute();
// Expected output:
// "The final number is 9"
firstValueFromHowever, you might want to take the first value as it arrives without waiting an Observable to complete, thus you can
use firstValueFrom. The firstValueFrom will resolve a Promise with the first value that was emitted from the
Observable and will immediately unsubscribe to retain resources. The firstValueFrom will also reject with an
{@link EmptyError} if the Observable completes with no values emitted.
import { interval, firstValueFrom } from 'rxjs';
async function execute() {
const source$ = interval(2000);
const firstNumber = await firstValueFrom(source$);
console.log(`The first number is ${firstNumber}`);
}
execute();
// Expected output:
// "The first number is 0"
<span class="informal">Both functions will return a Promise that rejects if the source Observable errors. The Promise will reject with the same error that the Observable has errored with.</span>
If you don't want Promises created by lastValueFrom or firstValueFrom to reject with {@link EmptyError} if there
were no emissions before completion, you can use the second parameter. The second parameter is expected to be an object
with defaultValue parameter. The value in the defaultValue will be used to resolve a Promise when source Observable
completes without emitted values.
import { firstValueFrom, EMPTY } from 'rxjs';
const result = await firstValueFrom(EMPTY, { defaultValue: 0 });
console.log(result);
// Expected output:
// 0
Only use lastValueFrom function if you know an Observable will eventually complete. The firstValueFrom function should
be used if you know an Observable will emit at least one value or will eventually complete. If the source Observable
does not complete or emit, you will end up with a Promise that is hung up, and potentially all of the state of an async
function hanging out in memory. To avoid this situation, look into adding something like {@link timeout}, {@link take},
{@link takeWhile}, or {@link takeUntil} amongst others.