Find all you need to in one, single place.

The ultimate guide to Observables and/vs Promises (+RxJS7).

Oleh Baranovskyi
7 min readDec 11, 2021

During this article, you’ll get a full insight into how promises differ with regard to observables. We’ll try to cover all you need to know in one place. Each topic is going to be followed by code examples. So, you’ll better believe you won’t get bored here.

If you are ready, let’s dive right in.

Topics to cover

  • Pull vs Push
  • Single vs Multiple
  • Async Promise and Async/Sync Observable
  • Convert Promise to Observable
    - from operator
    - fromFetch operator
  • Convert Observable to promise
    - toPromise method (deprecated)
    - firstValueFrom — first emitted value
    - lastValueFrom — last emitted value
  • Does RxJS work with promises?
  • Promise boxed into Observable is Hot
    - Let’s make it cold using defer operator
  • Unicast vs Multicast
  • Initial load vs lack of functionality

# Pull vs Push

Pull and Push are two different protocols that describe how a data Producer can communicate with a data Consumer.

What is Pull? In Pull systems, the Consumer determines when it receives data from the data Producer. The Producer itself is unaware of when the data will be delivered to the Consumer.

What is Push? In Push systems, the Producer determines when to send data to the Consumer. The Consumer is unaware of when it will receive that data.

Promises

Promises are the most common type of Push system in JavaScript today. A Promise (the Producer) delivers a resolved value to registered callbacks (the Consumers), but unlike functions, it is the Promise which is in charge of determining precisely when that value is “pushed” to the callbacks.

Observables

Observable is a new Push system for JavaScript. An Observable is a Producer of multiple values, “pushing” them to Observers (Consumers).

You can find out more about the push and pull-based systems by referring to the documentation, where I took a bare minimum.

# Single vs Multiple

Observables

Observables can emit either multiple values over a period of time or a single value at a time.

Promises

Unlike observables promises only can emit a single value at a time.

# Async Promise Vs Async/Sync Observable

Promise is always asynchronous. Here is an example that demonstrates this in action:

Observable might be either asynchronous or synchronous. Let’s take a look at an asynchronous example first:

Here is an example of synchronous observable:

# Convert Promise to Observable

`from` operator

The easiest way to convert promise to observable is to use from operator.

Of course, there is no need to unsubscribe us observable will be complete as soon as the promise will be resolved.

`fromFetch` operator

Promise returned by fetch will resolve as soon as the response's headers are received.

Important to note, that some parts are still in the experimental state. To get more familiar you can refer to the documentation.

More about fetch API find on MDN Web Docs.

# Convert Observable to Promise

`toPromise`— from RxJS7 is deprecated

Converts Observable to a Promise, which will be resolved once observable complete. The last emission will be returned as a resolved value.

If observable won’t complete, we will end up with a promise that won’t be resolved.

`firstValueFrom` — first emitted value

Converts an observable to a promise by subscribing to the observable,
and returning a promise that will resolve as soon as the first value arrives from the observable. The subscription will then be closed.

  1. firstValueFrom doesn’t wait for the observable to complete:

2. When observable completes before the values arrive, the promise will be rejected with EmptyError

3. When observable completes before the values arrive, we get an EmptyError. To prevent this, we might pass a second argument which basically is a fallback.

`lastValueFrom` — last emitted value

For the most part, it’s pretty similar to fisrtstValueFrom, but works with the last value.

1. Converts observable to promise, and waits for it to complete. When observable completes, the returned promise is resolved with the last emitted value.

2. If observable completes before the values arrive, returned promise will be rejected with the EmptyError

3. When observable completes before the values arrive, we get an EmptyError. To prevent this, we might pass a second argument which basically is a fallback.

# Does RxJS work with Promises?

Yes, it does. Many operators work with Promises as well as with observables.

Here’s a little case study of this strategy in action:

I’ve picked the most popular, such as:
- switchMap
- exhaustMap
- mergeMap
- concatMap
- combineLatest
- forkJoin
- withLatestFrom
- merge
- concat

and only withLatestFrom doesn’t work with Promise.

You can check whether the operator accepts the Promise by checking if it accepts the SubscribableOrPromise.

And now, you’re probably thinking how awesome this is, but not really. Important to note here is that you better use the ObservableInput (Observable) instead, as SubscribableOrPromise is deprecated, and it will be removed in version 8.

More details on this can be found in the documentation.

# Promise boxed into Observable is Hot

Promise is eager and will start to produce value right away, even if there is no subscription. Here is an example:

The value produced by Promise is produced outside and runs before any subscription, thus it’s hot.

Promise itself will run only once, and produced value will be shipped to all subscribers.

Let’s make it cold using `defer` operator:

To make it cold, we can apply the defer operator:

# Unicast vs Multicast

Observable is unicast because each observer has its own instance of the data producer. On the other hand, Observable is multicast if each observer receives notifications from the same producer.

Here is an example of unicast observable:

and this is multicasting:

Obviously, that Promise is unicast.

# Initial load vs lack of functionality

RxJS is not a native API. That means we would need to load the library initially. Obviously, this is a downside. Let’s take a look at the size:

The size isn’t big, but the room for doubt still is, especially, when you work on a smaller project.

Let’s take a look from the other side. Promises are native API, but the promises themselves do not provide even half of the functionality that exists in RxJS. Something like canceling, retries, debouncing, throttling, different types of combinations, etc. is not in place. This means, if you’re interested in similar functionality, you would need to bring up some new library. Likely, the best option from the promise libraries is bluebird.js.

Let’s take a closer look at bluebird.js size:

I don’t know what about you, but I didn’t expect bluebird’s size to be bigger than the size of RxJS.

Even though RxJS is not a native API but I would consider using it in advance, as it might pay off in the future.

Conclusion

I hope you have discovered something new for yourself during this article. Please feel free to reach out if you have any questions.

If you liked the article, hit the 👏 👏👏 button, and subscribe if you’re interested in such content as there will be more.

--

--

Oleh Baranovskyi
Oleh Baranovskyi

Written by Oleh Baranovskyi

Frontend Lead & Architect | Web community manager https://obaranovskyi.com/

No responses yet