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.
- then: When a Promise is fulfilled successfully
- catch: When there is an error with promise and is rejected
- finally: Called in either of the cases(resolve or reject), So can be used for cleanup/freeing resources
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