Implementing Promise.all and Promise.race in JavaScript
Throughout the past couple of months, much of the JavaScript development I have been involved with has revolved around Promises.
Promises are a simple abstraction to handle deferred and asynchronous computations.
This lunchtime, I decided to see how one would go about implementing two concepts that are present in the Promise standard: all
and race
.
Promise.all
This function is used to compose multiple supplied promises into a single returned promise that provides the resulting values in array form. Looking at the implementation documented below, you can see how relatively easy it is to compose these promises together, adding logic in between resolutions to accumulate the final value returned.
const all = (...promises) => {
const results = [];
const merged = promises.reduce(
(acc, p) => acc.then(() => p).then(r => results.push(r)),
Promise.resolve(null)
);
return merged.then(() => results);
};
We can use this solution in a couple of contrived examples, initially highlighting the ‘happy path’ of three separate promises being resolved before moving on to handling the case where a promise is rejected.
const p1 = Promise.resolve('foo');
const p2 = 'bar';
const p3 = new Promise((res, rej) => {
setTimeout(res, 100, 'baz');
});
all(p1, p2, p3).then(([a1, a2, a3]) => console.log(a1, a2, a3));
// "foo" "bar" "baz"
const p4 = Promise.reject('qux');
all(p1, p2, p3, p4)
.then(([a1, a2, a3]) => console.log(a1, a2, a3))
.catch(e => console.log(e));
// "qux"
Promise.race
Following on from the all
implementation, we can implement the race
function that is also present in the standard.
This function returns the first completed (resolved or rejected) promise from a supplied collection.
Wrapping the supplied promises within a new promise whose resolved and rejected functions are called based on their actions allows you to see how the race condition works.
const race = (...promises) =>
new Promise((res, rej) => {
promises.forEach(p => p.then(res).catch(rej));
});
We can highlight this implementation in an example, first depicting the ‘happy path’, followed by a rejection which is returned due to ‘winning the race’.
const p1 = new Promise((res, rej) => {
setTimeout(res, 100, 'foo');
});
const p2 = new Promise((res, rej) => {
setTimeout(res, 200, 'bar');
});
race(p1, p2).then(a => console.log(a)); // "foo"
const p3 = new Promise((res, rej) => {
setTimeout(rej, 50, 'baz');
});
race(p1, p2, p3)
.then(a => console.log(a))
.catch(e => console.log(e)); // "baz"