JavaScript promises in detail with examples

JavaScript promises were introduced in ES2015 to make working with asynchronous code easier. Promises solve problems with earlier async patterns(Callbacks). Callbacks were difficult to debug and there were problem with callback hell.

As promises were introduced in a later stage, Most vanilla JavaScript/Node APIs still use callback based approach. Most libraries have incorporated promise-based APIs.

What are promises #

Promises in JavaScript work like promises in real life. Suppose my mom asks me to buy eggs while coming from the office. I promise to bring the same. I may fulfill the promise by bringing the eggs or I can break the promise if I forget to bring them.
Similar way when we create a promise in JavaScript, promise is a token which can be fulfilled or rejected at a later stage. Promises are quite useful in Network calls or I/O operations.

const token = doNetworkCall();
token
.then(() => {
// when fulfilled
})
.catch(() => {
// when rejected
});

Here are few examples which will help make promises from existing APIs if promises are not supported.

Create a promise #

A Promise is a global object available in runtimes with ES2015 support. To create a promise, the Promise constructor takes one callback function as a parameter. The Callback function has two parameters resolve, reject functions, resolve is called with data after successful completion, and reject is called with a reason.

Create a promise from NodeJS style APIs #
const promise = new Promise((resolve, reject) => {
someAsyncOperation((error, result) => {
if (error) {
reject(error); // Reject with reason, if error
}
resolve(result); // Resolve with value, if there is no error
});
});
Create a promise from setTimeout #

setTimeout has been existed for long time and it is callback based. We can convert setTimeout to Promise as below.

function delay(time) {
return new Promise((resolve, reject) => {
setTimeout(resolve, time);
});
}

Run Promise and catch errors #

A promise instance has 3 main methods, then, catch and finally.

promise
.then((result) => {
console.log(result);
})
.catch((err) => {
console.error(err);
})
.finally(() => {
// Runs after fullfilment or reject
// doCleanup()
});

Run promises sequentially #

Promises are chainable, so multiple promises can run sequentically by chaining .then on promise instance.

fetch(`https://cat-fact.herokuapp.com/facts`)
.then((result) => {
return result.json();
})
.then((result) => {
console.log(result);
})
.catch((err) => {
console.error(err);
})
.finally(() => {
// doCleanup()
});

with async support #

try {
const res = await fetch(`https://cat-fact.herokuapp.com/facts`); // need to wrap in a async functions
const json = await res.json();
} catch (e) {
console.error(err);
} finally {
// doCleanup()
}

Run Promises parallelly #

Promises can run parallelly using Promise.all, it takes a promise iterator as a parameter. If one or more promise is rejected Promise.all is also rejected.

Promise.all([promise1, promise2, promise3])
.then((res) => {
console.log(res); // all promises successful, res is iteraor of results in same sequence
})
.catch((err) => {
console.error(err); // one or more promise failed
});

All settles #

Promise.all is handy when we want all promises to execute successfully. What if some promises are rejected and still other promises results is still useful. In that can Promise.allSettled is useful. Promise.allSettled is never rejected,
After all promises are settled an iterator of results is returned with status.

Promise.allSettled([promise1, promise2, promise3]).then((results) => {
console.log(results.map((result) => result.status)); // all promises executed, either resolved or rejected result = {status: 'fulfilled'|'rejected, value: promiseValue}
});

Promise.any #

Promise.any can be used qhen same data is requested from multiple sources and we can use data from the fastest source. Promise.any is resolved as soon as any one promise is resolved.

const promises = [promise1, promise2];
Promise.any(promises)
.then(() => {
// Executed as soon as any promise is resolved
})
.catch(() => {
// if none of the promises are resolved
});

Promise.race #

Promise.race resolved or rejected as soon as first promise is resolved or rejected. This is different from Promise.any in way that if fastest promise is rejected in Promise.any we will wait for second(and onwards) fastest promise to fulfill, while in case of Promise.race we wait for just the fastest promise, resolve or rejection status is not considered.

const promises = [promise1, promise2];
Promise.race(promises)
.then(() => {
// if fasted settled promise is resolved
})
.catch(() => {
// if fastest settled rpromise is rejected
});

Since you've made it this far, sharing this article on your favorite social media network would be highly appreciated ! For feedback, please ping me on Twitter.

Published