Promise.resolve()

I have one more Promise method to go over today, Promise.resolve(). This method is related to Promise.reject() in that it also is only concerned with a single Promise (as opposed to an iterable of Promises). However, where Promise.reject() is about deliberately rejecting a Promise, Promise.resolve() is (probably unsurprisingly) used to deliberately resolve a Promise and define the value the resolved Promise carries with it. This method is useful for debugging or, as we have seen throughout a bunch of my Promise blog posts, demonstrating examples of Promise behavior throughout a Promise chain.

Syntax

Promise.resolve(value);

Parameters

  • value - Argument to be resolved by this Promise. Can also be a Promise or a thenable to resolve.

Return Value

Promise.resolve() returns a Promise that is resolved with the given value. If that value itself is a Promise, then that Promise is returned.

Promise.resolve('Woohoo!').then(
  (result) => { console.log(result) },
  (error) => { console.error(error) } // not called
);
// Expected output: "Woohoo!"

Here's an example of basic usage of Promise.resolve(). I've passed it the value of a string, "Woohoo!". It then follows the .then() instance method. Because we are forcing a resolution, the value we passed gets returned as the result, and the resolve handler function just logs "Woohoo!". The error handler function does not run.

But now let's look at a slightly more advanced case: What would happen if the value passed to Promise.resolve() is itself a Promise?

const promise1 = Promise.resolve("Hi there");
const promise2 = Promise.resolve(promise1);

promise2.then(
  (result) => { console.log(`result: ${result}`) },
  (error) => { console.error(error) }
);

console.log(`promise1 === promise2 ? ${(promise1 === promise2)}`);
/*
 * Expected result:
 * promise1 === promise2 ? true
 * result: Hi there
 */

So what we're seeing in this example is that if the value given to Promise.resolve() is itself a Promise, then that Promise is returned. promise2 accepts the resolved promise1 as its value, where promise1's returned value is the string "Hi there". I also put in a check to see whether the return values of promise1 and promise2 are strictly equal to one another. It turns out they are. Also note that this check logs its result before the .then() handler on promise2. This happens because Promise handler functions are called asynchronously.

Thenables

Now, let's take a look at thenables, which is something that has a then method.

const thenable = {
  then: function(onFulfill, onReject) {
    onFulfill('Woohoo!');
  }
};

const promise = Promise.resolve(thenable);

console.log(promise instanceof Promise); // Expected result: true, object casted as a Promise

promise.then(
  (result) => { console.log(result) },
  (error) => { console.error(error) } // not called
);
// Expected result: "Woohoo!"

In this example I've defined an object and saved it to the variable thenable. This has one method named then. This is a function that accepts two callback functions as its parameters, named onFulfill and onReject, and calls onFulfill with the string value "Woohoo!".

Below that, I've defined a variable named promise and its value is the result of Promise.resolve(). I've passed the thenable object as Promise.resolve()'s value argument.

Below the promise definition, I'm checking whether the value returned by promise's Promise.resolve() method is an instance of a Promise. This returns true, demonstrating that an object with a then method actually gets casted as a Promise when it gets passed to Promise.resolve().

Finally we can follow the then method in the thenable object, logging out the result value as usual, giving us the string "Woohoo!".

Now I'm going to slightly modify this code to see what happens when the thenable throws an error before its callback function runs.

const thenable = {
  then: function(onFulfill) {
    throw new TypeError('Throwing an error');
    onFulfill('Woohoo!');
  }
};

const promise = Promise.resolve(thenable);
promise.then(
  (result) => { console.log(result) }, // not called
  (error) => { console.error(error) }
);
// Expected result: TypeError: Throwing an error

This function throws a TypeError with the message "Throwing an error". This error is thrown before the onFulfill callback gets called.

We then pass thenable as the value for Promise.resolve() and assign that to the variable promise. When we follow the .then() instance methods on promise, the error handler function logs the thrown TypeError. The resolve handler ((result) => { console.log(result) },) is not called. This happens because we threw the TypeError before the callback function inside of the thenable then method.

By contrast, let's throw the error after the callback function:

const thenable = {
  then: function(onFulfill) {
    onFulfill('Woohoo!');
    throw new TypeError('Throwing an error');
  }
};

const promise = Promise.resolve(thenable);
promise.then(
  (result) => { console.log(result) },
  (error) => { console.error(error) } // not called
);
// Expected result: "Woohoo!"

This code is exactly the same except the onFullfill callback method is called before the TypeError is thrown. onFulfill is given the string value "Woohoo!". In the .then() instance method below, the resolve handler function returns this string.

Avoiding Infinite Recursion

A warning here on thenable objects: do not call Promise.resolve() on a thenable that resolves to itself. This will lead to infinite recursion as the Promise.resolve() method will continuously attempt to flatten an infinitely-nested Promise:

Don't do the following!!!

const thenable = {
  then: (onResolve, onReject) => {
    onResolve(thenable);
  }
}

Promise.resolve(thenable); // Stop!!! This is bad!! Don't run this code.

From MDN: The Promise.resolve() method returns a Promise object that is resolved with a given value. If the value is a Promise, that Promise is returned; if the value is a thenable (i.e. has a .then() method), the returned Promise will "follow" that thenable, adopting its eventual state; otherwise the returned Promise will be fulfilled with the value. This function flattens nested layers of Promise-like objects (e.g. a Promise that resolves to a Promise that resolves to something) into a single layer.

See more examples and further documentation here.