ES6 Promise.all () error handle - Is .settle () required?

Let's say I have Promise.all() that handles two promises. If one promise gives an error, but the other resolves, I would like to handle errors based on the situation after Promise.all() installed.

ES6 Promises lacks a calculation method, I assume for a good reason. But I cannot help but think that the .settle() method will make this problem much easier for me.

Am I mistaken about this or am I extending ES6 Promises using the calculation method that needs to be done here?

An example of how I think about using .settle() :

 Promise.all([Action1,Action2]) .settle(function(arrayOfSettledValues) //if 1 failed but not 2, handle //if 2 failed but not 1, handle //etc.... ) 
+8
source share
1 answer

Am I going this way wrong or is the extension of the ES6 Promises settlement method the right solution?

You cannot directly use Promise.all() to generate a behavior like .settle() that gives you all the results, whether they are rejected or not, because Promise.all() is "fast Promise.all() " and is returned as soon as the first promise is rejected and only the reason for rejection is returned, none of the other results.

So, you need something else. Often the easiest way to solve this problem is to simply add the .then() handler to any operation that creates your promise array, so that it intercepts any deviations and turns them into fulfilled promises with a specific value that you can check. But this type of solution depends on the implementation, since it depends on which type of value you are returning, so it is not completely universal.

If you want a universal solution, then something like .settle() quite useful.

You cannot use structure:

 Promise.all([...]).settle(...).then(...); 

Note (added in 2019). It seems that in the standard Promise Promise.allSettled() as a standard implementation of behavior similar to settlement. You can see more about this at the end of this answer.

Because Promise.all() rejects when the first promise you pass rejects and returns only that rejection. .settle() works as follows:

 Promise.settle([...]).then(...); 

And, if you're interested, here is a pretty simple implementation of Promise.settle() :

 // ES6 version of settle Promise.settle = function(promises) { function PromiseInspection(fulfilled, val) { return { isFulfilled: function() { return fulfilled; }, isRejected: function() { return !fulfilled; }, isPending: function() { // PromiseInspection objects created here are never pending return false; }, value: function() { if (!fulfilled) { throw new Error("Can't call .value() on a promise that is not fulfilled"); } return val; }, reason: function() { if (fulfilled) { throw new Error("Can't call .reason() on a promise that is fulfilled"); } return val; } }; } return Promise.all(promises.map(function(p) { // make sure any values are wrapped in a promise return Promise.resolve(p).then(function(val) { return new PromiseInspection(true, val); }, function(err) { return new PromiseInspection(false, err); }); })); } 

In this implementation, Promise.settle() always resolves (never rejects) and resolves using an array of PromiseInspection objects that allows you to check each individual result to see if it was resolved or rejected, and what was the value or reason for each. It works by attaching a .then() handler to each promise passed, which processes the resolution or rejection from that promise and puts the result in a PromiseInspection object which then becomes the resolved value of the promise.

Then you would use this implementation as follows;

 Promise.settle([...]).then(function(results) { results.forEach(function(pi, index) { if (pi.isFulfilled()) { console.log("p[" + index + "] is fulfilled with value = ", pi.value()); } else { console.log("p[" + index + "] is rejected with reasons = ", pi.reason()); } }); }); 

For your information, I wrote another version of .settle myself, which I call .settleVal() and it is often easier for me to use it when you do not need the actual reason for the rejection, you just want to find out if the given array slot was rejected or not. In this version, you are passing a default value that should be replaced with any rejected promise. Then you just get a flat array of return values ​​and any of them that have a default value set where they are rejected. For example, you can often choose rejectVal null or 0 or "" or {} and this makes it easier to work with the results. Here's the function:

 // settle all promises. For rejected promises, return a specific rejectVal that is // distinguishable from your successful return values (often null or 0 or "" or {}) Promise.settleVal = function(rejectVal, promises) { return Promise.all(promises.map(function(p) { // make sure any values or foreign promises are wrapped in a promise return Promise.resolve(p).then(null, function(err) { // instead of rejection, just return the rejectVal (often null or 0 or "" or {}) return rejectVal; }); })); }; 

And then you use it like this:

 Promise.settleVal(null, [...]).then(function(results) { results.forEach(function(pi, index) { if (pi !== null) { console.log("p[" + index + "] is fulfilled with value = ", pi); } }); }); 

This is not a complete replacement for .settle() because sometimes you may need to find out the actual reason why it was rejected, or you cannot easily distinguish the rejected value from the non-rejected value. But I believe that in more than 90% of cases it is easier to use.


Here is my last simplification for .settle() which leaves instanceof Error in the returned array as a means of distinguishing between allowed values ​​and rejected errors:

 // settle all promises. For rejected promises, leave an Error object in the returned array Promise.settleVal = function(promises) { return Promise.all(promises.map(function(p) { // make sure any values or foreign promises are wrapped in a promise return Promise.resolve(p).catch(function(err) { let returnVal = err; // instead of rejection, leave the Error object in the array as the resolved value // make sure the err is wrapped in an Error object if not already an Error object if (!(err instanceof Error)) { returnVal = new Error(); returnVal.data = err; } return returnVal; }); })); }; 

And then you use it like this:

 Promise.settleVal(null, [...]).then(function(results) { results.forEach(function(item, index) { if (item instanceof Error) { console.log("p[" + index + "] rejected with error = ", item); } else { console.log("p[" + index + "] fulfilled with value = ", item); } }); }); 

This can be a complete replacement for .settle() for all cases where instanceof Error never the permitted value of your promises (and this really should not be).


Promise Standards

It seems that since 2019 .allSettled() become the standard for this type of behavior. And here is the polyfill:

 if (!Promise.allSettled) { Promise.allSettled = function(promises) { let wrappedPromises = Array.from(promises).map(p => this.resolve(p).then( val => ({ state: 'fulfilled', value: val }), err => ({ state: 'rejected', reason: err }) ) ); return this.all(wrappedPromises); } } 

Usage would be like this:

 let promises = [...]; // some array of promises, some of which may reject Promise.allSettled(promises).then(results => { for (let r of results) { if (r.state === 'fulfilled') { console.log('fulfilled:', r.val); } else { console.log('rejected:', r.err); } } }); 

Note that Promise.allSettled() by itself always resolves, never rejects, although subsequent .then() handlers may issue or return a rejected promise to reject the entire chain.

As of June 2019, this is not yet in the current Chrome desktop browser, but is planned for the next version (for example, later in 2019).

+16
source

All Articles