0

Async functions somehow postpone(schedule) throw statements from immediate execution, while normal statements like console.log are executed immediately.

async function test() {
  console.log('before throw')
  throw 'error in test()'
}

test()
console.log('exit')

I would expect test() to be executed till the end, so no 'exit' to be printed to the console.

Barmar
  • 741,623
  • 53
  • 500
  • 612
Maksim Shamihulau
  • 1,219
  • 1
  • 15
  • 17
  • 1
    What do you mean by "*executed till the end*"? What is the "end" - especially in the context of asynchronous code? Where are you running this and what output are you getting? – Bergi Dec 28 '22 at 23:17
  • 2
    `async` wraps the function body in a `Promise`. The promise catches the exception and passes it to the `.catch()` callback function. If you don't use `.catch()`, the exception is ignored. – Barmar Dec 28 '22 at 23:20
  • @Bergi by the 'end' I meant error will be thrown before 'exit' printed, as if `test()` would be just a regular function. In this particular case both `console.log` printed: 'before throw' followed by 'exit' and then `throw` throws an error. – Maksim Shamihulau Dec 28 '22 at 23:35
  • @Barmar the error is not ignored as you saying, it's printed in the console in red instead! – Maksim Shamihulau Dec 28 '22 at 23:42
  • 1
    @MaksimShamihulau I was fooled by the Stack Snippet console simulator, which doesn't show asynchronous errors. – Barmar Dec 28 '22 at 23:43

2 Answers2

1

By design, an uncaught error thrown in an async function will reject the promise that the async function returns. Quoting Mozilla Contributors:

Return value

A Promise which will be resolved with the value returned by the async function, or rejected with an exception thrown from, or uncaught within, the async function.

So at the time the exception is thrown, it is caught by the async function and dealt with to settle the promise it returns. The exception has been handled (not postponed), and the async function returns after which execution happily continues with console.log('test').

There is however another mechanism whereby the host will trigger an "unhandled promise rejection" event when a rejected promise has no rejection handler, which is the case in your code. This may look like your exception was delayed, but it is in fact a different mechanism which repeats the reason that the concerned rejected promise has. This event will only be triggered when the callstack is empty and it is clear that the rejected promise has no rejection handler.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • Just a recap 1) I had misconception thinking that rejected promises are normal errors/exceptions, just because they are also displayed in red in the Dev console the same as other regular errors/exceptions. So, Rejected promises are NOT errors/exceptions! 2) `throw ...` in `async function` translates/wraps into `return Promise.reject(...)`. 3) All unhandled `Promise.reject(...)` calls are displayed in red by JavaScript engine by default. Bonus: this was also useful for me https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await – Maksim Shamihulau Dec 30 '22 at 09:54
  • (1) and (3): Rejected promises are ... promises, i.e. they are objects. What you see in the console is not the promise, but a `unhandledrejection` event with a description that includes the "reason" of the rejected promise. (2) Not exactly. The exception may occur after an `await` when the `async` function has *already* returned a promise, which is in a pending state. After the `await`, the `async` function *resumes*, and if then the error occurs, that already existing promise will *transition* into a rejected state. – trincot Dec 30 '22 at 10:51
  • by `2)` I meant something like `async function myfunc() { throw 'error'; console.log('exit') }` that will no go past `throw` statement in any case, so `'exit'` will not be printed. In short, `throw 'error'` would be like `return Promise.reject('error')`. – Maksim Shamihulau Dec 30 '22 at 15:48
  • I understood what you meant. – trincot Dec 30 '22 at 16:04
0

Async functions somehow postpone(schedule) throw statements from immediate execution

No, they don't. The exception is thrown immediately when the function is called, and it does reject the promise returned by test(). A rejection which your code is ignoring, leading to an unhandled rejection! This is the (delayed) error you are seeing in red in the devtools console.
Notice that the behaviour of throw is no different than if the statement was executed after the test code had awaited something.

I would expect test() to throw an error as if test would be just a regular function, so no 'exit' to be printed to the console.

Well but test is not a regular function! Like any async function, it returns a promise. It will never throw an error when called.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I'm not asking about solution on how to avoid printing 'exit' or whatever. I'm asking a bit advanced stuff on why the example I provided above behaves the way it behaves. – Maksim Shamihulau Dec 28 '22 at 23:45
  • @MaksimShamihulau Edited. Why it behaves this way? Because that's the only way to make sense if you had an `await` in the function before `throw`ing an exception. – Bergi Dec 29 '22 at 00:52