Understanding Async/Await Under the Hood: The JavaScript Runtime Explained

Understanding Async/Await Under the Hood: The JavaScript Runtime Explained

async/await is syntactic sugar — but knowing what it desugars to explains every surprising behavior you encounter. Why does a resolved Promise run before setTimeout(fn,0)? Why does await never truly block? How does the engine know where to resume execution? Understanding the runtime makes you a better async programmer.

TL;DR: async function returns Promise immediately. await suspends current function, saves stack frame, releases to event loop. Promise callbacks are microtasks — run before next macrotask. setTimeout is a macrotask. V8 uses coroutines for await resumption. Microtask queue drains completely before any macrotask.

async/await desugared — what it really is

// This async/await code:
async function fetchUser(id) {
  const response = await fetch('/api/users/'+id);
  const user = await response.json();
  return user;
}

// Is equivalent to this generator + Promise chain:
function fetchUser(id) {
  return new Promise((resolve,reject) => {
    const gen = (function*() {
      try {
        const response = yield fetch('/api/users/'+id);
        const user = yield response.json();
        resolve(user);
      } catch(err) { reject(err); }
    })();
    // Drive the generator forward on each yield resolution
    function step(value) {
      const result = gen.next(value);
      if(result.done) return;
      result.value.then(step,reject);
    }
    step();
  });
}

Event loop phases and microtasks

// Phase order:
// 1. Call stack (synchronous code)
// 2. Microtask queue: Promise callbacks, queueMicrotask()
// 3. Macrotask queue: setTimeout, setInterval, I/O
// Microtask queue DRAINS COMPLETELY between macrotasks

console.log('1');           // Call stack
setTimeout(()=>console.log('2'),0); // Macrotask queue
Promise.resolve().then(()=>console.log('3')); // Microtask queue
Promise.resolve()
  .then(()=>console.log('4'))
  .then(()=>console.log('5')); // Two microtasks chained
console.log('6');           // Call stack
// Output: 1, 6, 3, 4, 5, 2
// 3,4,5 = microtasks ALL drain before macrotask '2'

Why await never blocks

// await pauses the FUNCTION but not the thread
async function main() {
  console.log('A');           // Runs immediately
  const data = await fetch('/api/data'); // Suspends main()
                              // V8 saves the stack frame
                              // Event loop continues!
  console.log('C');           // Resumes after fetch resolves
}
main();
console.log('B');             // Runs WHILE main is suspended
// Output: A, B, C
// JavaScript is single-threaded — await releases control
// Node.js event loop can process other I/O while waiting

Microtask starvation — the hidden danger

// Infinite microtask loop starves event loop!
function bad() {
  Promise.resolve().then(bad); // Creates endless microtasks!
}
bad();
// Event loop never processes I/O, timers, or anything else!
// setTimeout callbacks never run

// Same problem with recursive async:
async function flood() {
  while(true) await Promise.resolve(); // Blocks event loop!
}
// Fix: use setImmediate() to yield to event loop occasionally
async function better(items) {
  for(let i=0; isetImmediate(r)); // Yield!
  }
}
  • ✅ Promise microtasks execute before setTimeout macrotasks
  • ✅ Microtask queue drains completely before next macrotask
  • ✅ await suspends function frame, releases thread to event loop
  • ✅ async function always returns a Promise (even if you return a value)
  • ❌ Never create infinite microtask chains — starves event loop
  • ❌ Never mix callbacks and promises in same function

External reference: MDN Microtask guide.

Recommended Reading

Designing Data-Intensive Applications — The bible of distributed systems and production engineering at scale.

The Pragmatic Programmer — Timeless engineering wisdom every senior developer needs.

Affiliate links. We earn a small commission at no extra cost to you.

Free Weekly Newsletter

🚀 Join 2,000+ Senior Developers

Get expert-level JavaScript, Python, AWS, system design and AI secrets every week. Zero fluff, pure signal.

✓ No spam✓ Unsubscribe anytime✓ Expert-level only

Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply