[javascript] Handling errors in Promise.all

I have an array of Promises that I'm resolving with Promise.all(arrayOfPromises);

I go on to continue the promise chain. Looks something like this

existingPromiseChain = existingPromiseChain.then(function() {
  var arrayOfPromises = state.routes.map(function(route){
    return route.handler.promiseHandler();
  });
  return Promise.all(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
  // do stuff with my array of resolved promises, eventually ending with a res.send();
});

I want to add a catch statement to handle an individual promise in case it errors, but when I try, Promise.all returns the first error it finds (disregards the rest), and then I can't get the data from the rest of the promises in the array (that didn't error).

I've tried doing something like ..

existingPromiseChain = existingPromiseChain.then(function() {
      var arrayOfPromises = state.routes.map(function(route){
        return route.handler.promiseHandler()
          .then(function(data) {
             return data;
          })
          .catch(function(err) {
             return err
          });
      });
      return Promise.all(arrayOfPromises)
    });

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
      // do stuff with my array of resolved promises, eventually ending with a res.send();
});

But that doesn't resolve.

Thanks!

--

Edit:

What the answers below said were completely true, the code was breaking due to other reasons. In case anyone is interested, this is the solution I ended up with ...

Node Express Server Chain

serverSidePromiseChain
    .then(function(AppRouter) {
        var arrayOfPromises = state.routes.map(function(route) {
            return route.async();
        });
        Promise.all(arrayOfPromises)
            .catch(function(err) {
                // log that I have an error, return the entire array;
                console.log('A promise failed to resolve', err);
                return arrayOfPromises;
            })
            .then(function(arrayOfPromises) {
                // full array of resolved promises;
            })
    };

API Call (route.async call)

return async()
    .then(function(result) {
        // dispatch a success
        return result;
    })
    .catch(function(err) {
        // dispatch a failure and throw error
        throw err;
    });

Putting the .catch for Promise.all before the .then seems to have served the purpose of catching any errors from the original promises, but then returning the entire array to the next .then

Thanks!

This question is related to javascript es6-promise

The answer is


Promise.all is all or nothing. It resolves once all promises in the array resolve, or reject as soon as one of them rejects. In other words, it either resolves with an array of all resolved values, or rejects with a single error.

Some libraries have something called Promise.when, which I understand would instead wait for all promises in the array to either resolve or reject, but I'm not familiar with it, and it's not in ES6.

Your code

I agree with others here that your fix should work. It should resolve with an array that may contain a mix of successful values and errors objects. It's unusual to pass error objects in the success-path but assuming your code is expecting them, I see no problem with it.

The only reason I can think of why it would "not resolve" is that it's failing in code you're not showing us and the reason you're not seeing any error message about this is because this promise chain is not terminated with a final catch (as far as what you're showing us anyway).

I've taken the liberty of factoring out the "existing chain" from your example and terminating the chain with a catch. This may not be right for you, but for people reading this, it's important to always either return or terminate chains, or potential errors, even coding errors, will get hidden (which is what I suspect happened here):

Promise.all(state.routes.map(function(route) {
  return route.handler.promiseHandler().catch(function(err) {
    return err;
  });
}))
.then(function(arrayOfValuesOrErrors) {
  // handling of my array containing values and/or errors. 
})
.catch(function(err) {
  console.log(err.message); // some coding error in handling happened
});

I wrote a npm library to deal with this problem more beautiful. https://github.com/wenshin/promiseallend

Install

npm i --save promiseallend

2017-02-25 new api, it's not break promise principles

const promiseAllEnd = require('promiseallend');

const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)];
const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)};

// input promises with array
promiseAllEnd(promises, {
    unhandledRejection(error, index) {
        // error is the original error which is 'error'.
        // index is the index of array, it's a number.
        console.log(error, index);
    }
})
    // will call, data is `[1, undefined, 2]`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// input promises with object
promiseAllEnd(promisesObj, {
    unhandledRejection(error, prop) {
        // error is the original error.
        // key is the property of object.
        console.log(error, prop);
    }
})
    // will call, data is `{k1: 1, k3: 2}`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// the same to `Promise.all`
promiseAllEnd(promises, {requireConfig: true})
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [false, true, false]})
    // won't call
    .then(data => console.log(data))
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [true, false, false]})
    // will call, data is `[1, undefined, 2]`.
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

————————————————————————————————

Old bad api, do not use it!

let promiseAllEnd = require('promiseallend');

// input promises with array
promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)])
    .then(data => console.log(data)) // [1, undefined, 2]
    .catch(error => console.log(error.errorsByKey)) // {1: 'error'}

// input promises with object
promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)})
    .then(data => console.log(data)) // {k1: 1, k3: 2}
    .catch(error => console.log(error.errorsByKey)) // {k2: 'error'}

For those using ES8 that stumble here, you can do something like the following, using async functions:

var arrayOfPromises = state.routes.map(async function(route){
  try {
    return await route.handler.promiseHandler();
  } catch(e) {
    // Do something to handle the error.
    // Errored promises will return whatever you return here (undefined if you don't return anything).
  }
});

var resolvedPromises = await Promise.all(arrayOfPromises);

Alternately, if you have a case where you don't particularly care about the values of the resolved promises when there is one failure but you still want them to have run, you could do something like this which will resolve with the promises as normal when they all succeed and reject with the failed promises when any of them fail:

function promiseNoReallyAll (promises) {
  return new Promise(
    async (resolve, reject) => {
      const failedPromises = []

      const successfulPromises = await Promise.all(
        promises.map(
          promise => promise.catch(error => {
            failedPromises.push(error)
          })
        )
      )

      if (failedPromises.length) {
        reject(failedPromises)
      } else {
        resolve(successfulPromises)
      }
    }
  )
}

You would need to know how to identify an error in your results. If you do not have a standard expected error, I suggest that you run a transformation on each error in the catch block that makes it identifiable in your results.

try {
  let resArray = await Promise.all(
    state.routes.map(route => route.handler.promiseHandler().catch(e => e))
  );

  // in catch(e => e) you can transform your error to a type or object
  // that makes it easier for you to identify whats an error in resArray
  // e.g. if you expect your err objects to have e.type, you can filter
  // all errors in the array eg
  // let errResponse = resArray.filter(d => d && d.type === '<expected type>')
  // let notNullResponse = resArray.filter(d => d)

  } catch (err) {
    // code related errors
  }

We can handle the rejection at the individual promises level, so when we get the results in our result array, the array index which has been rejected will be undefined. We can handle that situation as needed, and use the remaining results.

Here I have rejected the first promise, so it comes as undefined, but we can use the result of the second promise, which is at index 1.

_x000D_
_x000D_
const manyPromises = Promise.all([func1(), func2()]).then(result => {_x000D_
    console.log(result[0]);  // undefined_x000D_
    console.log(result[1]);  // func2_x000D_
});_x000D_
_x000D_
function func1() {_x000D_
    return new Promise( (res, rej) => rej('func1')).catch(err => {_x000D_
        console.log('error handled', err);_x000D_
    });_x000D_
}_x000D_
_x000D_
function func2() {_x000D_
    return new Promise( (res, rej) => setTimeout(() => res('func2'), 500) );_x000D_
}
_x000D_
_x000D_
_x000D_


You can always wrap your promise returning functions in a way that they catches failure and returning instead an agreed value (e.g. error.message), so the exception won't roll all the way up to the Promise.all function and disable it.

async function resetCache(ip) {

    try {

        const response = await axios.get(`http://${ip}/resetcache`);
        return response;

    }catch (e) {

        return {status: 'failure', reason: 'e.message'};
    }

}

Using Async await -

here one async function func1 is returning a resolved value, and func2 is throwing a error and returning a null in this situation, we can handle it how we want and return accordingly.

const callingFunction  = async () => {
    const manyPromises = await Promise.all([func1(), func2()]);
    console.log(manyPromises);
}


const func1 = async () => {
    return 'func1'
}

const func2 = async () => {
    try {
        let x;
        if (!x) throw "x value not present"
    } catch(err) {
       return null
    }
}

callingFunction();

Output is - [ 'func1', null ]


Have you considered Promise.prototype.finally()?

It seems to be designed to do exactly what you want - execute a function once all the promises have settled (resolved/rejected), regardless of some of the promises being rejected.

From the MDN documentation:

The finally() method can be useful if you want to do some processing or cleanup once the promise is settled, regardless of its outcome.

The finally() method is very similar to calling .then(onFinally, onFinally) however there are couple of differences:

When creating a function inline, you can pass it once, instead of being forced to either declare it twice, or create a variable for it.

A finally callback will not receive any argument, since there's no reliable means of determining if the promise was fulfilled or rejected. This use case is for precisely when you do not care about the rejection reason, or the fulfillment value, and so there's no need to provide it.

Unlike Promise.resolve(2).then(() => {}, () => {}) (which will be resolved with undefined), Promise.resolve(2).finally(() => {}) will be resolved with 2. Similarly, unlike Promise.reject(3).then(() => {}, () => {}) (which will be fulfilled with undefined), Promise.reject(3).finally(() => {}) will be rejected with 3.

== Fallback ==

If your version of JavaScript doesn't support Promise.prototype.finally() you can use this workaround from Jake Archibald: Promise.all(promises.map(p => p.catch(() => undefined)));


That's how Promise.all is designed to work. If a single promise reject()'s, the entire method immediately fails.

There are use cases where one might want to have the Promise.all allowing for promises to fail. To make this happen, simply don't use any reject() statements in your promise. However, to ensure your app/script does not freeze in case any single underlying promise never gets a response, you need to put a timeout on it.

function getThing(uid,branch){
    return new Promise(function (resolve, reject) {
        xhr.get().then(function(res) {
            if (res) {
                resolve(res);
            } 
            else {
                resolve(null);
            }
            setTimeout(function(){reject('timeout')},10000)
        }).catch(function(error) {
            resolve(null);
        });
    });
}

To continue the Promise.all loop (even when a Promise rejects) I wrote a utility function which is called executeAllPromises. This utility function returns an object with results and errors.

The idea is that all Promises you pass to executeAllPromises will be wrapped into a new Promise which will always resolve. The new Promise resolves with an array which has 2 spots. The first spot holds the resolving value (if any) and the second spot keeps the error (if the wrapped Promise rejects).

As a final step the executeAllPromises accumulates all values of the wrapped promises and returns the final object with an array for results and an array for errors.

Here is the code:

_x000D_
_x000D_
function executeAllPromises(promises) {_x000D_
  // Wrap all Promises in a Promise that will always "resolve"_x000D_
  var resolvingPromises = promises.map(function(promise) {_x000D_
    return new Promise(function(resolve) {_x000D_
      var payload = new Array(2);_x000D_
      promise.then(function(result) {_x000D_
          payload[0] = result;_x000D_
        })_x000D_
        .catch(function(error) {_x000D_
          payload[1] = error;_x000D_
        })_x000D_
        .then(function() {_x000D_
          /* _x000D_
           * The wrapped Promise returns an array:_x000D_
           * The first position in the array holds the result (if any)_x000D_
           * The second position in the array holds the error (if any)_x000D_
           */_x000D_
          resolve(payload);_x000D_
        });_x000D_
    });_x000D_
  });_x000D_
_x000D_
  var errors = [];_x000D_
  var results = [];_x000D_
_x000D_
  // Execute all wrapped Promises_x000D_
  return Promise.all(resolvingPromises)_x000D_
    .then(function(items) {_x000D_
      items.forEach(function(payload) {_x000D_
        if (payload[1]) {_x000D_
          errors.push(payload[1]);_x000D_
        } else {_x000D_
          results.push(payload[0]);_x000D_
        }_x000D_
      });_x000D_
_x000D_
      return {_x000D_
        errors: errors,_x000D_
        results: results_x000D_
      };_x000D_
    });_x000D_
}_x000D_
_x000D_
var myPromises = [_x000D_
  Promise.resolve(1),_x000D_
  Promise.resolve(2),_x000D_
  Promise.reject(new Error('3')),_x000D_
  Promise.resolve(4),_x000D_
  Promise.reject(new Error('5'))_x000D_
];_x000D_
_x000D_
executeAllPromises(myPromises).then(function(items) {_x000D_
  // Result_x000D_
  var errors = items.errors.map(function(error) {_x000D_
    return error.message_x000D_
  }).join(',');_x000D_
  var results = items.results.join(',');_x000D_
  _x000D_
  console.log(`Executed all ${myPromises.length} Promises:`);_x000D_
  console.log(`— ${items.results.length} Promises were successful: ${results}`);_x000D_
  console.log(`— ${items.errors.length} Promises failed: ${errors}`);_x000D_
});
_x000D_
_x000D_
_x000D_


if you get to use the q library https://github.com/kriskowal/q it has q.allSettled() method that can solve this problem you can handle every promise depending on its state either fullfiled or rejected so

existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
  return route.handler.promiseHandler();
});
return q.allSettled(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
//so here you have all your promises the fulfilled and the rejected ones
// you can check the state of each promise
arrayResolved.forEach(function(item){
   if(item.state === 'fulfilled'){ // 'rejected' for rejected promises
     //do somthing
   } else {
     // do something else
   }
})
// do stuff with my array of resolved promises, eventually ending with a res.send();
});

Promise.allSettled

Instead of Promise.all use Promise.allSettled which waits for all promises to settle, regardless of the result

_x000D_
_x000D_
let p1 = new Promise(resolve => resolve("result1"));
let p2 = new Promise( (resolve,reject) => reject('some troubles') );
let p3 = new Promise(resolve => resolve("result3"));

// It returns info about each promise status and value
Promise.allSettled([p1,p2,p3]).then(result=> console.log(result));
_x000D_
_x000D_
_x000D_

Polyfill

_x000D_
_x000D_
if (!Promise.allSettled) {
  const rejectHandler = reason => ({ status: 'rejected', reason });
  const resolveHandler = value => ({ status: 'fulfilled', value });

  Promise.allSettled = function (promises) {
    const convertedPromises = promises
      .map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
    return Promise.all(convertedPromises);
  };
}
_x000D_
_x000D_
_x000D_


I've found a way (workaround) to do this without making it sync.

So as it was mentioned before Promise.all is all of none.

so... Use an enclosing promise to catch and force resolve.


      let safePromises = originalPrmises.map((imageObject) => {
            return new Promise((resolve) => {
              // Do something error friendly
              promise.then(_res => resolve(res)).catch(_err => resolve(err))
            })
        })
    })

    // safe
    return Promise.all(safePromises)

As @jib said,

Promise.all is all or nothing.

Though, you can control certain promises that are "allowed" to fail and we would like to proceed to .then.

For example.

  Promise.all([
    doMustAsyncTask1,
    doMustAsyncTask2,
    doOptionalAsyncTask
    .catch(err => {
      if( /* err non-critical */) {
        return
      }
      // if critical then fail
      throw err
    })
  ])
  .then(([ mustRes1, mustRes2, optionalRes ]) => {
    // proceed to work with results
  })

ES2020 introduces new method for the Promise type: Promise.allSettled()
Promise.allSettled gives you a signal when all the input promises are settled, which means they’re either fulfilled or rejected. This is useful in cases where you don’t care about the state of the promise, you just want to know when the work is done, regardless of whether it was successful.

const promises = [
  fetch('/api-call-1'),
  fetch('/api-call-2'),
  fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.

const result = await Promise.allSettled(promises);
console.log(result.map(x=>x.status));
// ['fulfilled', 'fulfilled', 'rejected']

Read more in the v8 blog post https://v8.dev/features/promise-combinators


NEW ANSWER

const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));

FUTURE Promise API


Not the best way to error log, but you can always set everything to an array for the promiseAll, and store the resulting results into new variables.

If you use graphQL you need to postprocess the response regardless and if it doesn't find the correct reference it'll crash the app, narrowing down where the problem is at

const results = await Promise.all([
  this.props.client.query({
    query: GET_SPECIAL_DATES,
  }),
  this.props.client.query({
    query: GET_SPECIAL_DATE_TYPES,
  }),
  this.props.client.query({
    query: GET_ORDER_DATES,
  }),
]).catch(e=>console.log(e,"error"));
const specialDates = results[0].data.specialDates;
const specialDateTypes = results[1].data.specialDateTypes;
const orderDates = results[2].data.orders;