- Direct Execution/Conversion
- Use
from to directly convert a previously created Promise into an Observable.
import { from } from 'rxjs';
// getPromise() is called once, the promise is passed to the Observable
const observable$ = from(getPromise());
observable$ will be a hot Observable, which can effectively replay the Promise value to subscribers.
It is a hot Observable because the producer (in this case, the Promise) is created outside the Observable. Multiple subscribers will share the same Promise. If the internal Promise has been resolved, new subscribers to the Observable will immediately receive its value.
- Deferred Execution per Subscription
- Use
defer with a Promise factory function as input to defer the creation and conversion of the Promise into an Observable.
import { defer } from 'rxjs';
// getPromise() is called every time someone subscribes to the observable$
const observable$ = defer(() => getPromise());
observable$ will be a cold Observable.
It is a cold Observable because the producer (the Promise) is created inside the Observable. Each subscriber will create a new Promise by calling the given Promise factory function.
This allows you to create a Promise observable$ without immediately creating and executing the Promise, and without sharing this Promise with multiple subscribers. Each subscriber can effectively call from(promiseFactory()).subscribe(subscriber). Thus, each subscriber creates their own new Promise and converts it to a new Observable, attaching themselves to this new Observable.
- Many Operators Directly Accept Promises
- Most combination operators (e.g.,
merge, concat) or transformation operators (e.g., forkJoin, combineLatest, switchMap, mergeMap, concatMap, catchError) directly accept Promises. If you use any of these operators, you don't need to wrap the Promise first (but to create a cold Observable, you may still need to use defer).
// Execute two promises simultaneously
forkJoin(getPromise(1), getPromise(2)).pipe(
switchMap(([v1, v2]) => v1.getPromise(v2)) // map to nested Promise
)
Check the documentation or implementation to see if the operator you are using accepts ObservableInput or SubscribableOrPromise.
type ObservableInput<T> = SubscribableOrPromise<T> | ArrayLike<T> | Iterable<T>;
// Note the PromiseLike ----------------------------------------------------v
type SubscribableOrPromise<T> = Subscribable<T> | Subscribable<never> | PromiseLike<T> | InteropObservable<T>;
Difference between from and defer example: https://stackblitz.com/edit/rxjs-6rb7vf
const getPromise = val => new Promise(resolve => {
console.log('Promise created for', val);
setTimeout(() => resolve(`Promise Resolved: ${val}`), 5000);
});
// the execution of getPromise('FROM') starts here, when you create the promise inside from
const fromPromise$ = from(getPromise('FROM'));
const deferPromise$ = defer(() => getPromise('DEFER'));
fromPromise$.subscribe(console.log);
// the execution of getPromise('DEFER') starts here, when you subscribe to deferPromise$
deferPromise$.subscribe(console.log);
defer is likely the operator most people are looking for, as many applications rely on Observables to maintain a cold state and trigger data fetching upon subscription. However, for certain use cases, from remains a viable option, such as when you want to create a Promise once during initialization and propagate its value through an Observable that is subscribed to multiple times, without creating and executing the Promise again for each subscriber.