Tutorial hero
Lesson icon

Use defer in RxJS to Make Converted Promises Lazy

Originally published September 27, 2023 Time 2 mins

A behavior of Observables that you typically discover a short while after working with RxJS is that they are executed lazily. That means that if you are performing some operation in an observable, that code won’t be executed until you actually subscribe to the observable.

For example, if you execute this code:

const observable = new Observable((subscriber) => {
  console.log("hello");
});

Nothing will be logged to the console. Nothing will happen until you actually call observable.subscribe() at which point the message will be logged to the console.

On the other hand, if we were to do a similar thing in a Promise:

const promise = new Promise((resolve) => {
  console.log("hello");
  resolve(true);
})

The message will be logged immediately, even if we aren’t actually using that Promise. The actual order of execution will look like this:

console.log("first");

const promise = new Promise((resolve) => {
  console.log("second");
  resolve("last");
});

promise.then((value) => console.log(value))

console.log("third");

So, the Promise still keeps the resolved value, and it will be handled asynchronously in the then after everything else has executed synchronously, but it is important to note that the body of the Promise (where second is logged) is executed immediately.

A surprise might come when you convert a Promise to an Observable:

const getMyPromise = () => {
  return new Promise((resolve) => {
    console.log('still eager');
    resolve(true);
  });
};

const observable = from(getMyPromise());

…and you find that the Promise is still executed eagerly without needing to subscribe to the observable!

To deal with this, you can use defer from RxJS:

const getMyPromise = () => {
  return new Promise((resolve) => {
    console.log('lazy again');
    resolve(true);
  });
};

const observable = from(defer(() => getMyPromise()));

observable.subscribe();

Now lazy again won’t actually be logged until we explicitly call observable.subscribe().

This is one of those things that often might not make that much of a difference whether something is executed lazily or eagerly. But, if you are in the mental model of Observables where things are not executed until they are subscribed, you might mistakenly assume this is the case for Promises that you have converted to Observables.

It’s important to be aware of the difference and understand how to address the issue if necessary - especially if you are doing things like executing network requests.

Learn to build modern Angular apps with my course