0

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);
    }
})();
Poyoman
  • 1,652
  • 1
  • 20
  • 28
  • 1
    [This answer](https://stackoverflow.com/questions/7310521/node-js-best-practice-exception-handling/36703397#36703397) *might* help. – freedomn-m Apr 17 '23 at 16:49
  • Why are you using `await` inside the `Promise.all` arguments? – Bergi Apr 17 '23 at 16:55
  • @HeikoTheißen with node v18.15.0 i get this output : ``` > Uncaught Error: here at fct (REPL3:2:11) > catched Error: here at fct (REPL3:2:11) (node:70851) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1) (Use `node --trace-warnings ...` to show where the warning was created) ``` – Poyoman Apr 17 '23 at 17:13
  • 1
    @freedomn-m Actually no, it doesn't – Bergi Apr 17 '23 at 17:16
  • 1
    @Bergi thanks for the feedback - I did a quick search on async exceptions and that was a detailed answer. – freedomn-m Apr 17 '23 at 17:30
  • @Bergi it's because i didn't want to make intermediate variable – Poyoman Apr 18 '23 at 07:45

2 Answers2

3

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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for your explication easy to understand, and references. I think i will still need to dive a bit more in it to perfectly understand what's happening here ^^ – Poyoman Apr 17 '23 at 17:19
0

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.

InSync
  • 4,851
  • 4
  • 8
  • 30