6

I am trying to run a for loop which queues a bunch of asynchronous requests. Once all the requests complete, independent of whether they resolve or reject, I want to then run some code. I am trying to take advantage of the async/await pattern, as it looks nicer. :)

This is what I am doing:

var promises = []
for ( item in list ) {
    prom = AsyncFunction( item )
    promises.push(prom)
}

await Promise.all(promises)

doMoreAfter()

However, some of the promises fail, and the second that happens Promise.all() fails aswell.

I want to simply ignore any failed promises, and have the next code run after all the promises have completed.

I found solutions like this.

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

But it doesn't look like it works when trying to turn it into the async/await format.

await Promise.all(promises.map(p => p.catch(e => e)))

What am I missing?

Shawn Tabrizi
  • 12,206
  • 1
  • 38
  • 69
  • Well, `Promise.all()` rejects as soon as the first promise you pass to it rejects so the ONLY way to make it wait for all promises to finish is to keep any promise you pass to `Promise.all()` from rejecting. When I want this type of behavior, I just use on of [these `Promise.settle()` versions](https://stackoverflow.com/questions/36605253/es6-promise-all-error-handle-is-settle-needed/36605453#36605453) which gives you the desired behavior built-in to a utility function rather than hand coded separately each time. – jfriend00 Nov 26 '17 at 06:44

3 Answers3

12

I feel like the best you can do is this:

var promises = [a, b, c];
promises = promises.map(p => p().catch(e => undefined));

values = await Promise.all(promises);
console.log(values);  // ["a", undefined, "c"]

https://jsfiddle.net/DerekL/h5Lmxaqq/

Or you can create an ignore function:

function ignore(promise){
    return promise.catch(e => undefined);
}

async function main(){
    var promises = [a(), b(), c()];

    var values = await Promise.all(promises.map(ignore));
    console.log(values);
}

https://jsfiddle.net/DerekL/mwkww7w9/

Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • Thanks. It seems in general, when dealing with multiple promises in a loop, that using await/async is not very good. For example, displaying the value returned from the Async function as soon as it comes back. `await` blocks the loop, but `.then()` does not. Is this a correct understanding of the current state of things? Maybe I should just avoid the async/await syntax for this specific problem... – Shawn Tabrizi Nov 26 '17 at 02:14
  • The `async/await` syntax works great with `Promise.all`, however your case is a bit special and working with raw promises is probably a better idea. – Derek 朕會功夫 Nov 26 '17 at 02:20
  • In your last example, can't you just do `var values = await Promise.all(promises.map(ignore));`? – jfriend00 Nov 26 '17 at 07:21
  • @jfriend00 No, because `ignore` accepts promises and `a`, `b` & `c` are functions, but it's definitely possible to modify the `ignore` function to automatically executes the task. – Derek 朕會功夫 Nov 26 '17 at 07:23
  • Your last code examples shows `promises` as an array of promises, not functions and that's how your code uses them. Wait, you just edited something. – jfriend00 Nov 26 '17 at 07:26
  • @jfriend00 I agree I shouldn't have used that name. I have modified the answer to match the variable name :) – Derek 朕會功夫 Nov 26 '17 at 07:27
0

I tried running your example like this: https://jsfiddle.net/804ogadu/3/

var a = Promise.resolve('a');
var b = Promise.resolve('b')
var c = Promise.reject('errc');

async function test(){
const t = await Promise.all([a, b, c].map(p => p.catch(e => e)));
console.log(t);
}

test();

I do get the required result

['a,'b','errc']
Atul
  • 2,170
  • 23
  • 24
0

Did you consider using Promise.allSettled()? (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled#using_promise.allsettled)

Raanan Avidor
  • 3,533
  • 4
  • 25
  • 32