10

I have some issues to handle multiple rejections in "parallel". How to handle rejection in a async function when we "await in parallel".
Here an example :

function in_2_sec(number) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('Error ' + number);
        }, 2000);
    }) 
}

async function f1() {
    try {
        let a = in_2_sec(3);
        let b = in_2_sec(30);
        return await a + await b; // awaiting in "parallel"
    } catch(err) {
        console.log('Error', err);
        return false;
    }
}

async function f2() {
    try {
        let a = await Promise.all([in_2_sec(3), in_2_sec(30)]);
        return a[0] + a[1];
    } catch(err) {
        console.log('Error', err);
        return false;
    }
}

// f1().then(console.log) // UnhandledPromiseRejectionWarning
// f2().then(console.log) // Nice


f1() create a UnhandledPromiseRejectionWarning in node, because the second rejection (b) is not handled.
f2() works perfectly, Promise.all() do the trick, but how to make a f2() with only async/await syntax, without Promise.all() ?

Smirow
  • 125
  • 2
  • 8
  • 1
    `await` "waits for the promise to resolve". In `f1`, you are waiting for each of the promises to resolve independently because you use `await` twice. Since you don't handle them at all, the behaviour is logical. – Sebas Apr 23 '17 at 08:46
  • 1
    @Sebas: Indeed. I think the question is how to handle that logical behavior elegantly... :-) – T.J. Crowder Apr 23 '17 at 08:47
  • 1
    @Sebas sure it's perfectly logical, and as T.J. Crowder said the question is about to handle this behavior without `Promise.all()`. – Smirow Apr 23 '17 at 09:00
  • Wow, this question really threw me for a loop until I realized that the 2nd `await` is not executed at all. – Vicky Chijwani Feb 10 '18 at 22:08

2 Answers2

7

f2() works perfectly, Promise.all() do the trick, but how to make a f2() with only async/await syntax, without Promise.all()?

Not at all. Use Promise.all! This is what it was made for. Don't think of async/await of replacing promises - you still are using them, it's only syntactic sugar for then calls. There is no syntactic replacement for Promise.all.

Of course it's also possible to re-implement the behaviour of Promise.all yourself (relying on the Promise constructor and .then primitives), but you really don't want to do that (there are too many pitfalls).

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    The issue with `Promise.all()` is that it only return a single `reject`. In other words, we do not get to handle all rejections should there be more than 1 promise that got rejected. Only the first rejection will be returned. – Gavin Oct 30 '17 at 07:43
  • @NgHuiXiong If you want to handle all errors in parallel code, you have to handle the rejections individually on every promise before it is passed to `Promise.all`. – Bergi Oct 30 '17 at 16:42
3

The simplest thing I can think of, and I don't like it, is to put unconditional calls to catch in the error path, probably with a utility function.

With these utility functions:

function noop() {
}

function markHandled(...promises) {
    promises.forEach(p => p && p.catch(noop));
}

It's:

async function f2() {
    let a, b;
    try {
        a = in_2_sec(3);
        b = in_2_sec(30);
        return await a + await b;
    } catch(err) {
        console.log('Error', err);
        markHandled(a, b);
        return false;
    }
}

That way, we're explicitly ignoring rejections we don't care about, having caught the main error.

Example:

function in_2_sec(number) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('Error ' + number);
        }, 2000);
    }) 
}

function noop() {
}

function markHandled(...promises) {
    promises.forEach(p => p && p.catch(noop));
}

async function f2() {
    let a, b;
    try {
        a = in_2_sec(3);
        b = in_2_sec(30);
        return await a + await b;
    } catch(err) {
        console.log('Error', err);
        markHandled(a, b);
        return false;
    }
}

f2().then(console.log);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Didn't even think about it. Thanks. I'm don't sure that there is a very more elegant way. I'll stick with `Promise.all` for now. – Smirow Apr 23 '17 at 09:05
  • @Smirow: Yeah. There's been [some talk](https://esdiscuss.org/topic/how-about-awaiting-arrays) about adding to `await` to handle things like this. – T.J. Crowder Apr 23 '17 at 10:24