Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait until all promises complete even if some rejected

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 Promise.all doesn't leave any room for this, what is the recommended pattern for handling this, without using a promises library?

like image 780
Nathan Hagen Avatar asked Jul 15 '15 07:07

Nathan Hagen


People also ask

How do you wait for promises to complete?

You can use the async/await syntax or call the . then() method on a promise to wait for it to resolve. Inside of functions marked with the async keyword, you can use await to wait for the promises to resolve before continuing to the next line of the function.

How do you handle rejection promises?

We must always add a catch() , otherwise promises will silently fail. In this case, if thePromise is rejected, the execution jumps directly to the catch() method. You can add the catch() method in the middle of two then() methods, but you will not be able to break the chain when something bad happens.

Does promise all wait?

Using Promise. all. Promise. all waits for all fulfillments (or the first rejection).


2 Answers

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");
});
like image 91
Benjamin Gruenbaum Avatar answered Oct 07 '22 01:10

Benjamin Gruenbaum


Similar answer, but more idiomatic for ES6 perhaps:

const a = Promise.resolve(1);
const b = Promise.reject(new Error(2));
const c = Promise.resolve(3);

Promise.all([a, b, c].map(p => p.catch(e => e)))
  .then(results => console.log(results)) // 1,Error: 2,3
  .catch(e => console.log(e));


const console = { log: msg => div.innerHTML += msg + "<br>"};
<div id="div"></div>

Depending on the type(s) of values returned, errors can often be distinguished easily enough (e.g. use undefined for "don't care", typeof for plain non-object values, result.message, result.toString().startsWith("Error:") etc.)

like image 281
jib Avatar answered Oct 07 '22 01:10

jib