I was writing some code and found something interesting.
Example 1:
async function load(closure) {
try {
await closure();
} catch (error) {
console.log("error");
} finally {
console.log("finished");
}
}
load(async () => {
throw new Error();
});
console.log("hello");
This outputs:
hello
error
finished
However, if I call:
Example 2:
load(() => {
throw new Error();
});
The output is:
error
finished
hello
Can you explain what’s going on here?
I understand that calling await
queues execution to the next tick, so the first output is expected.
But in the second example, why is closure
behaving as a synchronous function if the caller is using await
?
This only happens when there’s an Error
thrown. If I call…
Example 3:
load(() => {
return 1;
});
… then closure
is called in the next tick as expected.
2
Answers
The difference between the two examples is due to the fact that the
async/await
syntax introduces an implicit delay in the function execution that causes thefinally
block to execute after theconsole.log("hello")
statement.Looking at the documentation
In your
Example 2
,await
is not used because the error happens before the creation of the promise (i.e. during the execution of theclosure
function). The function doesn’t return any value so there is no conversion to a native Promise.If
closure()
is notasync
and throws an exception, the promise has not a chance to be created, that’s why you seeerror
andfinished
logs first. In factawait
has not been used sinceclosure
function has not been fully executed.If
closure()
is notasync
and returns a value, it’s converted to a promise and the async function pauses until the next tick, that’s why you seehello
first.If
closure()
is notasync
and returns a rejected promise likePromise.rejected()
, you will seehello
first becauseawait
received a rejected promise and the async function pauses until the next tick like before.