I intend to use this space to document my promise modules, useful promise patterns, and how to solve common problems. For now though, you can see all my promise modules below.
Not accepting additions, but happy to take requests.
Promise#finally()
ponyfill - Invoked when the promise is settled regardless of outcomePromise.all()
but for Map
and Object
.then()
or .catch()
is calledPromise.try()
ponyfill - Starts a promise chainPromise.race()
setImmediate()
This is a good use-case for p-map
. You might ask why you can't just specify an array of promises. Promises represent values of a computation and not the computation itself - they are eager. So by the time p-map
starts reading the array, all the actions creating those promises have already started running. p-map
works by executing a promise-returning function in a mapper function. This way the promises are created lazily and can be concurrency limited. Check out p-all
instead if you're using different functions to get each promise.
const pMap = require('p-map');
const urls = [
'https://sindresorhus.com',
'https://avajs.dev',
'https://github.com',
…
];
console.log(urls.length);
//=> 100
const mapper = url => {
return fetchStats(url); //=> Promise
};
pMap(urls, mapper, {concurrency: 5}).then(result => {
console.log(result);
//=> [{url: 'https://sindresorhus.com', stats: {…}}, …]
});
Let's say you want to fetch some data, process it, and return both the data and the processed data.
The common approach would be to nest the promises:
const getData = id =>
Storage
.find(id)
.then(data => {
return process(data).then(result => {
return prepare(data, result);
});
});
But we can take advantage of Promise.all
:
const getData = id =>
Storage
.find(id)
.then(data => Promise.all([data, process(data)])
.then(([data, result]) => prepare(data, result));
And even simpler with async functions: (Requires Babel or Node.js 8)
const getData = async id => {
const data = await Storage.find(id);
return prepare(data, await process(data));
};
Bluebird#spread()
?Bluebird:
Promise.resolve([1, 2]).spread((one, two) => {
console.log(one, two);
//=> 1 2
});
Instead, use destructuring:
Promise.resolve([1, 2]).then(([one, two]) => {
console.log(one, two);
//=> 1 2
});
Bluebird.join()
?Bluebird:
Promise.join(p1, p2, p3, (r1, r2, r3) => {
// …
});
Instead, use an async function and destructuring:
const [r1, r2, r3] = await Promise.all([p1, p2, p3]);
// …
You might think you want to break out ("return early") when doing conditional logic in promise chains.
Here you would like to only run the onlyRunConditional
promises if conditional
is truthy.
alwaysRun1()
.then(() => alwaysRun2())
.then(conditional => conditional || somehowBreakTheChain())
.then(() => onlyRunConditional1())
.then(() => onlyRunConditional2())
.then(() => onlyRunConditional3())
.then(() => onlyRunConditional4())
.then(() => alwaysRun3());
You could implement the above by abusing the promise rejection mechanism. However, it would be better to branch out the chain instead. Promises can not only be chained, but also nested and unnested.
const runConditional = () =>
onlyRunConditional1()
.then(() => onlyRunConditional2())
.then(() => onlyRunConditional3())
.then(() => onlyRunConditional4());
alwaysRun1()
.then(() => alwaysRun2())
.then(conditional => conditional && runConditional())
.then(() => alwaysRun3());