I'm using babel
to transpile my node.js@0.10.x
code and I'm stuck with promises.
I need allSettled
-type functionality that I could use in q
and bluebird
or angular.$q
for example.
On babel's core-js Promise
, there is no allSettled
method.
Currently I'm using q.allSettled
as a workaround:
import { allSettled } from 'q';
Is there something like that in babel polyfill? Alternatively, which is a good algorithm for me to try to implement?
Alternatively, which is a good algorithm for me to try to implement?
- create a new promise with an executor function
- use a counter/result array in the scope of the executor
- register a then() callback with each parent promise saving the results in the array
- resolve/reject promise from step 1 when counter indicates that all parent promises are done
2019 Answer
There was a proposal to add this function to the ECMAScript standard, and it has been accepted! Check out the Promise.allSettled
docs for details.
Original Answer
If you take a look at the implementation of q.allSettled you'll see it's actually quite simple to implement. Here's how you might implement it using ES6 Promises:
function allSettled(promises) {
let wrappedPromises = promises.map(p => Promise.resolve(p)
.then(
val => ({ status: 'fulfilled', value: val }),
err => ({ status: 'rejected', reason: err })));
return Promise.all(wrappedPromises);
}
Here's my attempt at something similar, I have Newsletter service and in my case I wanted my allSettled promise to resolve with an array of all the results (rejections and resolutions), IN ORDER, once all the email_promises are settled (all the emails had gone out):
Newsletter.prototype.allSettled = function(email_promises) {
var allSettledPromise = new Promise(function(resolve, reject) {
// Keep Count
var counter = email_promises.length;
// Keep Individual Results in Order
var settlements = [];
settlements[counter - 1] = undefined;
function checkResolve() {
counter--;
if (counter == 0) {
resolve(settlements);
}
}
function recordResolution(index, data) {
settlements[index] = {
success: true,
data: data
};
checkResolve();
}
function recordRejection(index, error) {
settlements[index] = {
success: false,
error: error
};
checkResolve();
}
// Attach to all promises in array
email_promises.forEach(function(email_promise, index) {
email_promise.then(recordResolution.bind(null, index))
.catch(recordRejection.bind(null, index));
});
});
return allSettledPromise;
}
2020 answer:
What the other answers are trying to do is to implement Promise.allSettled
themselves. This was already done by the core-js project.
What you need to do is to make babel polyfill Promise.allSettled
for you via core-js. The way you configure it to do so is through @babel/preset-env
like so:
presets: [
['@babel/preset-env', {
useBuiltIns: 'usage',
corejs: {version: 3, proposals: true},
}],
],
In your build artifact this will add a call to require("core-js/modules/esnext.promise.all-settled")
which monkeypatches the .allSettled
function to the promises API.
Here's another take at the same functionality: spex.batch
The source code would be too much to re-post here, so here's just an example from the batch processing of how to use it:
var spex = require('spex')(Promise);
// function that returns a promise;
function getWord() {
return Promise.resolve("World");
}
// function that returns a value;
function getExcl() {
return '!';
}
// function that returns another function;
function nested() {
return getExcl;
}
var values = [
123,
"Hello",
getWord,
Promise.resolve(nested)
];
spex.batch(values)
.then(function (data) {
console.log("DATA:", data);
}, function (reason) {
console.log("REASON:", reason);
});
This outputs:
DATA: [ 123, 'Hello', 'World', '!' ]
Now let's make it fail by changing getWord
to this:
function getWord() {
return Promise.reject("World");
}
Now the output is:
REASON: [ { success: true, result: 123 },
{ success: true, result: 'Hello' },
{ success: false, result: 'World' },
{ success: true, result: '!' } ]
i.e. the entire array is settled, reporting index-bound results.
And if instead of reporting the entire reason we call getErrors()
:
console.log("REASON:", reason.getErrors());
then the output will be:
REASON: [ 'World' ]
This is just to simplify quick access to the list of errors that occurred.