DEV Community

Cover image for πŸš€ JavaScript Async/Await vs `.then()` : The Microtask & Event Loop Secret
keshav Sandhu
keshav Sandhu

Posted on

πŸš€ JavaScript Async/Await vs `.then()` : The Microtask & Event Loop Secret

Ever seen this and wondered why the output changes just by reordering lines?

async function hi() {
  return "there";
}

// Version A
const waitedhi = await hi();
console.log(waitedhi);

hi().then(res => console.log(res));

console.log("end");
Enter fullscreen mode Exit fullscreen mode

vs

// Version B
hi().then(res => console.log(res));

const waitedhi = await hi();
console.log(waitedhi);

console.log("end");
Enter fullscreen mode Exit fullscreen mode

πŸ‘€ Same code… different output. Why?


🧠 The Hidden Mechanism: Microtasks

Both await and .then() use Promises, and both schedule work in the microtask queue.

πŸ‘‰ Key rule:

Microtasks run in the order they are added (FIFO)


⚑ What await really does

await hi();
Enter fullscreen mode Exit fullscreen mode

is roughly:

hi().then(result => {
  // rest of your code
});
Enter fullscreen mode Exit fullscreen mode

So:

  • .then() β†’ schedules a microtask
  • await β†’ schedules a microtask (continuation)

πŸ”₯ Why order matters

Version A

  1. await schedules continuation
  2. resumes β†’ logs "there"
  3. .then() schedules another microtask
  4. "end" logs
  5. .then() runs

βœ… Output:

there
end
there
Enter fullscreen mode Exit fullscreen mode

Version B

  1. .then() β†’ microtask A
  2. await β†’ microtask B
  3. Run A β†’ Run B

βœ… Output:

there
there
end
Enter fullscreen mode Exit fullscreen mode

🧩 The Bigger Picture: Event Loop

To fully understand this, you need 3 things:

1️⃣ Call Stack

  • Executes synchronous code
  • Runs line by line
  • Example:
console.log("start");
Enter fullscreen mode Exit fullscreen mode

2️⃣ Microtask Queue (High Priority)

  • Promises (.then, await)
  • Runs immediately after stack is empty

3️⃣ Macrotask Queue (Low Priority)

  • setTimeout
  • setInterval
  • Runs after all microtasks are done

⚑ Golden Rule

JS finishes the call stack β†’ runs ALL microtasks β†’ then runs ONE macrotask


πŸ§ͺ Full Example

console.log("start");

setTimeout(() => console.log("timeout"), 0);

Promise.resolve().then(() => console.log("then"));

await Promise.resolve();

console.log("end");
Enter fullscreen mode Exit fullscreen mode

πŸ” Execution Flow

Step 1: Call Stack

start
Enter fullscreen mode Exit fullscreen mode

Step 2: Queues

Microtasks: [then, await-resume]
Macrotasks: [timeout]
Enter fullscreen mode Exit fullscreen mode

Step 3: Run Microtasks

then
end
Enter fullscreen mode Exit fullscreen mode

Step 4: Run Macrotask

timeout
Enter fullscreen mode Exit fullscreen mode

βœ… Final Output

start
then
end
timeout
Enter fullscreen mode Exit fullscreen mode

🎯 Final Takeaways

  • await = pause + schedule continuation as microtask
  • .then() = directly schedules microtask
  • Microtasks always run before macrotasks
  • Order of scheduling = order of execution

πŸ’¬ Once you understand this, async JavaScript stops feeling random and starts feeling predictable.

Top comments (0)