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.
Looking at the documentation
The expression is resolved in the same way as Promise.resolve(): it's always converted to a native Promise and then awaited
In your Example 2
, await
is not used because the error happens before the creation of the promise (i.e. during the execution of the closure
function). The function doesn't return any value so there is no conversion to a native Promise.
If closure()
is not async
and throws an exception, the promise has not a chance to be created, that's why you see error
and finished
logs first. In fact await
has not been used since closure
function has not been fully executed.
If closure()
is not async
and returns a value, it's converted to a promise and the async function pauses until the next tick, that's why you see hello
first.
If closure()
is not async
and returns a rejected promise like Promise.rejected()
, you will see hello
first because await
received a rejected promise and the async function pauses until the next tick like before.
function fn() {
throw new Error();
}
async function load() {
try {
await fn(); // like await Promise.resolve(fn())
} catch (error) {
console.error("
I came here because an error occurred during fn execution.
No promise has been created and await has not been used.
The async function has not been paused.
");
}
}
function fn() {
return Promise.reject("error");
}
// same as
async function fn() {
throw "error";
}
async function load() {
try {
await fn(); // like await Promise.reject()
} catch (error) {
console.error("
I came here because await received a rejected promise.
The async function has been paused until the next tick.
");
}
}