Zlatko
Zlatko

Reputation: 19569

Promise.allSettled in babel ES6 implementation

I'm using babel to transpile my [email protected] 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?

Upvotes: 23

Views: 21563

Answers (7)

Marko Bonaci
Marko Bonaci

Reputation: 5706

const allSettled = promises =>
  Promise.all(promises.map(promise => promise
    .then(value => ({ state: 'fulfilled', value }))
    .catch(reason => ({ state: 'rejected', reason }))
  ));

Or if you insist on polyfilling it:

if (Promise && !Promise.allSettled) {
  Promise.allSettled = function (promises) {
    return Promise.all(promises.map(function (promise) {
      return promise.then(function (value) {
        return { state: 'fulfilled', value: value };
      }).catch(function (reason) {
        return { state: 'rejected', reason: reason };
      });
    }));
  };
}

Taken from here

Upvotes: 5

Vikas Tiwari
Vikas Tiwari

Reputation: 70

my implementation will be below

Promise.prototype.myAllSettled = function (arr = []) {
  return new Promise(function processIterable(resolve, reject) {
    let result = [];
    arr.forEach((item) => {
      item
        .then((value) => {
          result.push({ status: "fulfilled", value: value });
          if (arr.length === result.length) resolve(result);
        })
        .catch((err) => {
          result.push({ status: "rejected", reason: `${err}` });
          if (arr.length === result.length) resolve(result);
        });
    });
  });
};

Upvotes: 1

Ivan Rubinson
Ivan Rubinson

Reputation: 3339

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.

Upvotes: 14

Michael Kropat
Michael Kropat

Reputation: 15217

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);
}

Upvotes: 44

vitaly-t
vitaly-t

Reputation: 25870

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.

Upvotes: 0

stujo
stujo

Reputation: 2109

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;
}

Upvotes: 1

the8472
the8472

Reputation: 43052

Alternatively, which is a good algorithm for me to try to implement?

  1. create a new promise with an executor function
  2. use a counter/result array in the scope of the executor
  3. register a then() callback with each parent promise saving the results in the array
  4. resolve/reject promise from step 1 when counter indicates that all parent promises are done

Upvotes: 2

Related Questions