When does a JavaScript async function actually complete execution?

I’m trying to understand the execution flow of async functions in JavaScript. It’s not as straightforward as I thought.

Here’s a simple example to illustrate my confusion:

async function myAsyncTask() {
  console.log('Task started');
  await new Promise(resolve => setTimeout(resolve, 2000));
  console.log('Task finished');
  return 'Done';
}

function runTask(task) {
  console.log('Begin');
  task();
  console.log('End');
}

runTask(myAsyncTask);

What’s the correct order of console logs here? Does myAsyncTask return immediately or after the 2-second delay?

I have a few theories:

  1. It waits for the whole async function to complete before moving on.
  2. It returns a promise right away and continues execution.
  3. It depends on how the first await is set up.

Can someone explain the exact behavior and underlying principles of async function execution? I’d really appreciate a clear explanation to help me grasp this concept better.

hey alex, async funcs can be confusing. they return a promise right away, but keep running in background. in ur example, you’d see ‘Begin’, ‘Task started’, ‘End’ immediately. then after 2 secs, ‘Task finished’. to actually wait for it, you’d need to use await or .then() on the returned promise. hope that helps!

The execution flow of async functions can be tricky to grasp at first. In your example, the order of console logs would be ‘Begin’ followed by ‘Task started’, then ‘End’, and finally ‘Task finished’ after a 2-second delay. When you call an async function, it returns a promise immediately, allowing its body to begin executing. However, when it reaches an ‘await’, it pauses so that the calling code can continue. In this case, runTask logs ‘End’ before myAsyncTask finishes. Essentially, task() acts as fire-and-forget; to wait for it to complete, you would need to await the promise or use .then() on it. This behavior enables asynchronous operations without blocking the main thread.

As someone who’s wrestled with async functions quite a bit, I can shed some light on this. The key thing to remember is that async functions always return a promise immediately, even if the function body hasn’t finished executing yet.

In your example, you’d see ‘Begin’, ‘Task started’, and ‘End’ logged right away. The ‘Task finished’ would only appear after the 2-second timeout. This is because runTask doesn’t wait for myAsyncTask to complete - it just fires it off and moves on.

To actually wait for the async function to finish, you’d need to either await it (if you’re in another async function) or use .then() on the returned promise. Something like:

runTask(async () => {
await myAsyncTask();
console.log(‘Really done now’);
});

This way, you’d ensure the entire async operation completes before moving on. It’s a common gotcha that trips up many developers, so don’t feel bad if it takes some time to wrap your head around it!