Wait until all promises complete even if some rejected

Nathan Hagen picture Nathan Hagen · Jul 15, 2015 · Viewed 160.4k times · Source

Let's say I have a set of Promises that are making network requests, of which one will fail:

// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]

Promise.all(arr)
  .then(res => console.log('success', res))
  .catch(err => console.log('error', err)) // This is executed   

Let's say I want to wait until all of these have finished, regardless of if one has failed. There might be a network error for a resource that I can live without, but which if I can get, I want before I proceed. I want to handle network failures gracefully.

Since Promises.all doesn't leave any room for this, what is the recommended pattern for handling this, without using a promises library?

Answer

Benjamin Gruenbaum picture Benjamin Gruenbaum · Jul 15, 2015

Update, you probably want to use the built-in native Promise.allSettled:

Promise.allSettled([promise]).then(([result]) => {
   //reach here regardless
   // {status: "fulfilled", value: 33}
});

As a fun fact, this answer below was prior art in adding that method to the language :]


Sure, you just need a reflect:

const reflect = p => p.then(v => ({v, status: "fulfilled" }),
                            e => ({e, status: "rejected" }));

reflect(promise).then((v => {
    console.log(v.status);
});

Or with ES5:

function reflect(promise){
    return promise.then(function(v){ return {v:v, status: "fulfilled" }},
                        function(e){ return {e:e, status: "rejected" }});
}


reflect(promise).then(function(v){
    console.log(v.status);
});

Or in your example:

var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]

Promise.all(arr.map(reflect)).then(function(results){
    var success = results.filter(x => x.status === "fulfilled");
});