JavaScript Promises 102 - The 4 Promise Methods

As I covered in JavaScript Promises 101, a promise in JavaScript is an object that represents the result of an operation that hasn't been completed yet, but will at some undetermined point in the future.

The Promise object itself (i.e. not an object on the prototype) has four methods available to it -

  • resolve()
  • reject()
  • all()
  • race()

Promise.resolve() #

Typically, when we create promises, we create them as functions that perform some asynchronous operation which will resolve when the operation is complete (or reject if it fails). The Promise.resolve() method, however, returns a Promise object that is immediately resolved. It accepts one argument, a value that can be passed to the .then() function.

Although this may seem counter to the purpose of promises, as there is no asynchronous operation being carried out, there are a few cases when this method is useful.

Example 1 - Returning Static Values as Promises #

One use case for the Promise.resolve() method is when we need to return a static value as a promise. To keep a chain of promises going, we need to return keep returning more promises. For example, in this sequence of fetch requests, we only move to the next .then() function when we return a promise that is resolved.

get(url)
.then((response) => {
response = JSON.parse(response);
const secondURL = response.data.url
return get( secondURL ); /* Return another Promise */
})
.then((response) => {
response = JSON.parse(response);
const thirdURL = response.data.url
return get( thirdURL ); /* Return another Promise */
})
.catch((err) => handleError(err) );

// Note: The get() function is a promise-ified XMLHTTPRequest
// (see https://bitsofco.de/javascript-promises-101/)

But what if, in the second step, we wanted to pass a value to the third step that was just static value, not a value received from a Promise? We can do that using Promise.resolve(), passing in the static value.

get(url)
.then((response) => {
response = JSON.parse(response);
const secondURL = response.data.url;
return Promise.resolve( secondURL ); /* Return a manually created Promise */
})
.then((secondURL) => get( secondURL ) )
.catch((err) => handleError(err) );

This way, we can keep the chain of promises going, even when working with regular functions or static values.

Example 2 - Starting Chains & Building Sequences #

Another use case for the Promise.resolve() method is if we want to start a promise chain, but the initial value we need isn’t a promise. An example of this is if we want to dynamically create a promise sequence, and we need an initial promise to start it all off.

Let's say we have an array of JSON files we need to get. In each of the files, there is an image url we need to retrieve and display on the page. So, for each item in the array of JSON files, we want to do the following (in the order they are in in the array) -

get(url)
.then((response) => {
response = JSON.parse(response);
displayImage(response.image);
})

If we are sure of the number of files we need to work with, we could just write out each of these tasks ourselves. But that wouldn't be very efficient, and what if we didn't know the exact number of files?

To solve this problem, we can loop through each of the files, and create the above promise chain. But to make sure that we execute each of the files in sequence, we need to have an initial Promise to which we append the subsequent promise chains. This is what it looks like -

const dataFiles = ['one.json', 'two.json', 'three.json'];

const sequence = Promise.resolve(); // Initial Promise

dataFiles.forEach((dataFile) => {

// Add to the initial promise sequence
sequence = sequence.then(() => {
return get(url).then((response) => {
response = JSON.parse(response);
displayImage(response.image);
});
});

});

Because we are appending to an existing promise, we needed that first resolved promise to get the chain started.

Promise.reject() #

The Promise.reject() method works like Promise.resolve(), but instead returns a promise object that is immediately rejected. It accepts one argument, the error that can be passed to the .catch() function.

The primary use case for this method is similar to the first use case for Promise.resolve(). It can be used to throw errors based on static values. For example, in a promise chain, we may want to throw an error based on a simple check we make, which is not necessarily based on an asynchronous operation.

get(url)
.then((response) => {

if ( response.data.length < 10 /* Can be any manual check */ ) {
return Promise.reject(new Error('Not enough data to continue'));
}

response = JSON.parse(response);
const secondURL = response.data.url
return get( secondURL );
})
.then((response) => {
response = JSON.parse(response);
const thirdURL = response.data.url
return get( thirdURL );
})
.catch((err) => handleError(err) );

Promise.all() #

The Promise.all() method offers us a way of dealing with multiple promises together. The method accepts an iterable list of items as it’s only argument, executes any promises within them, and returns a promise.

If all the promises within the iterable list are successfully resolved, it will return an array of the values to be read by the .then() function.

const promise1 = Promise.resolve('foo');
const promise2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'bar');
});
const promise3 = 'baz'; // Doesn't actually have to be a promise

Promise.all([promise1, promise2, promise3]).then((arrayOfResponses) => {
console.log(arrayOfResponses);
// ['foo', 'bar', 'baz']
});

If, however, even one of the promises in the iterable list is rejected, the entire promise is rejected.

const promise1 = Promise.resolve('foo');
const promise2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'bar');
});
const promise3 = Promise.reject('Error');

Promise.all([promise1, promise2, promise3]).then((arrayOfResponses) => {
console.log(arrayOfResponses);
}).catch((err) => {
console.log(err);
// 'Error'
});

This method is extremely useful when we have a group of asynchronous actions that are dependent on each other, so it becomes important that all be resolved together, or none at all.

Promise.race() #

Finally, the Promise.race() method offers an alternative way of dealing with multiple promises. Like Promise.all(), it accepts an iterable list of items as it’s only argument, executes any promises within them, and returns a promise.

However, this method will return as soon as any one of the promises in its list resolves or rejects, and passes the value or error message of that single promise to the .then() or .catch() function.

const promise1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'foo');
});
const promise2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'bar');
});
const promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 900, 'baz');
});

Promise.race([promise1, promise2, promise3]).then((response) => {
console.log(response);
// bar
});

The use case for this method is less obvious than the others. From what I can tell the only scenario in which it will be useful is if you're trying to fetch the same resource from different places, so in this case you can deal with the response from whichever is the quickest, and discard the rest.

Keep in touch KeepinTouch

Subscribe to my Newsletter 📥

Receive quality articles and other exclusive content from myself. You’ll never receive any spam and can always unsubscribe easily.

Elsewhere 🌐