Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does "throw Error" make a function run as synchronous?

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.

like image 354
Jose Carlos Ramírez Avatar asked Oct 24 '25 14:10

Jose Carlos Ramírez


1 Answers

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.
    ");
  }
}
like image 77
Olivier Boissé Avatar answered Oct 27 '25 07:10

Olivier Boissé