Back to Rxjs

Conversion to Promises

apps/rxjs.dev/content/deprecations/to-promise.md

7.8.24.2 KB
Original Source

Conversion to Promises

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.

Issues

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.

Use one of the two new functions

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}.

lastValueFrom

The 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.

Example

ts
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"

firstValueFrom

However, 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.

Example

ts
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>

Use default value

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.

ts
import { firstValueFrom, EMPTY } from 'rxjs';

const result = await firstValueFrom(EMPTY, { defaultValue: 0 });
console.log(result);

// Expected output:
// 0

Warning

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.