skip to Main Content

I tried this in a browser and node.js. I get an Uncaught (in promise) Error: here … I have no idea why the try catch can’t get it.

Anyone can solve this mystery ?

fct = async (msg) => {
    throw new Error(msg)
}

delayedValue = (value) => new Promise((resolve) => setTimeout(() => resolve(value), 1000));

(async () => {
    try {
        console.log(await Promise.all([
            fct(await delayedValue('here')),
            fct(await delayedValue('it is')),
        ]));
    } catch (e) {
        console.error('catched', e);
    }
})();

2

Answers


  1. In fct(await delayedValue('here')), await makes the IIFE wait 1 second for delayedValue('here'), which then passes 'here' to fct(), which in turn throws an error.

    At this time of the event loop, await delayedValue('it is') has yet to return anything, which means the IIFE is still waiting for it and haven’t execute catch.

    That being said, catch eventually catches the error, but one second late.

    Login or Signup to reply.
  2. The problem is the combination of awaits and un-awaited async function calls. The code is executed as

    const value1 = await delayedValue('here');
    const promise1 = fct(value1);
    const value2 = await delayedValue('it is');
    const promise2 = fct(value2);
    await Promise.all([promise1, promise2]);
    

    which is a well-known antipattern that causes exactly the problem you are experiencing. promise1 is a promise that is getting rejected before it gets a handler attached1 via Promise.all. It’s already being rejected while the delayedValue('it is') promise is awaited, and that’s causing an unhandled rejection error.

    It’s not clear how exactly you wanted the code to run, you might be looking for any of

    • const value1 = await delayedValue('here');
      const value2 = await fct(value1);
      const value3 = await delayedValue('it is');
      const value4 = await fct(value3);
      console.log([value2, value4]);
      
    • const [value1, value2] = await Promise.all([
        delayedValue('here'),
        delayedValue('it is'),
      ]);
      console.log(await Promise.all([
        fct(value1),
        fct(value2),
      ]));
      
    • console.log(await Promise.all([
        delayedValue('here').then(fct),
        delayedValue('it is').then(fct),
      ]));
      
    • const value1 = await delayedValue('here');
      const [value2, value4] = await Promise.all([
        fct(value1),
        delayedValue('it is').then(fct)
      ]);
      console.log([value2, value4]);
      

    All of these will fail fast and handle any error from any of the called functions immediately in a surrounding try/catch block.

    1: More precisely, the handler must be attached "immediately", during the same microtask (loop?), possibly after the promise is rejected – it’s fine when fct() returns an already-rejected promise. Either way, await delayedValue() is not acceptable before the rejection handler is attached.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search