100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

What Is the JavaScript Event Loop?

Learn how the JavaScript event loop, call stack, and microtask/macrotask queues determine async execution order.

hardQ27 of 224 in Web Development Est. time: 6 minsLast updated:
Open Code Lab

Expected Interview Answer

The event loop is the mechanism that lets single-threaded JavaScript handle asynchronous work by continuously checking whether the call stack is empty and, if so, pulling the next queued callback โ€” first from the microtask queue, then from the macrotask (callback) queue โ€” onto the stack to execute.

JavaScript runs on one call stack, so synchronous code always executes to completion before anything else runs. When you call an async API like setTimeout, fetch, or a Promise, the actual work is handed off to the browser or Node runtime, and only the completion callback is queued once that work finishes โ€” it does not block the stack. The event loop's job is to wait until the call stack is fully empty, then drain the entire microtask queue (Promise callbacks, queueMicrotask, async/await continuations) before taking a single task from the macrotask queue (setTimeout, setInterval, I/O callbacks, UI events). This ordering is why a Promise.then() callback always runs before a setTimeout(fn, 0) callback, even though both were queued at the same tick. Understanding this ordering is essential for reasoning about race conditions, render timing, and why long synchronous code blocks the entire UI.

  • Explains why Promise callbacks run before setTimeout callbacks
  • Clarifies why synchronous code can block rendering and I/O
  • Enables correct reasoning about async/await execution order
  • Helps diagnose real bugs like UI freezes from long synchronous loops

AI Mentor Explanation

The event loop is like a single umpire who can only watch one ball at a time on the field, but has two trays for follow-up actions: an urgent tray for DRS reviews and a regular tray for post-match paperwork. Every time the umpire finishes the current ball completely, they first clear the entire DRS review tray before ever glancing at the paperwork tray. Only once every pending review is resolved does one paperwork item get processed, and then the cycle repeats. That single-threaded, drain-urgent-before-one-regular-item pattern is exactly how the event loop prioritizes microtasks over macrotasks.

Step-by-Step Explanation

  1. Step 1

    Synchronous code runs to completion

    The call stack executes all synchronous statements before anything else can run.

  2. Step 2

    Async work is handed off

    APIs like setTimeout, fetch, or Promise executors delegate work to the runtime, which queues a callback when done.

  3. Step 3

    Microtask queue drains fully

    Once the call stack is empty, every pending Promise callback and queueMicrotask entry runs, even ones added during this drain.

  4. Step 4

    One macrotask runs, then repeat

    A single task like a setTimeout callback or UI event runs, then the loop checks the stack and microtask queue again.

What Interviewer Expects

  • Correct explanation of call stack, microtask queue, and macrotask queue
  • Clear statement that microtasks fully drain before a single macrotask runs
  • A worked example predicting console.log order across sync, Promise, and setTimeout
  • Awareness that long synchronous code blocks the entire loop, including UI updates

Common Mistakes

  • Claiming JavaScript is multi-threaded because of async behavior
  • Getting the ordering wrong between Promise.then and setTimeout(fn, 0)
  • Forgetting that the microtask queue can starve macrotasks if it keeps adding to itself
  • Not distinguishing the browser event loop from Node.js's phased event loop (timers, I/O, check phases)

Best Answer (HR Friendly)

โ€œThe event loop is how JavaScript, which only does one thing at a time, still handles things like network requests and timers without freezing. It runs your normal code first, then always clears out any pending Promise results before moving on to timer or event callbacks, which is why a Promise result usually shows up before a setTimeout callback even if they were both set up at the same moment.โ€

Code Example

Predicting execution order: sync, microtask, macrotask
console.log('1: sync start')

setTimeout(() => console.log('4: macrotask (setTimeout)'), 0)

Promise.resolve().then(() => console.log('3: microtask (Promise)'))

console.log('2: sync end')

// Output order: 1, 2, 3, 4
// Sync code always finishes first, then all microtasks drain,
// then exactly one macrotask runs.

Follow-up Questions

  • How does async/await map onto the microtask queue under the hood?
  • What is the difference between the browser event loop and Node.js's libuv-based event loop phases?
  • How can a recursive microtask starve the macrotask queue and freeze rendering?
  • Why does a long synchronous for-loop freeze the UI even with pending Promises?

MCQ Practice

1. Given the same tick, which runs first: a Promise.then callback or a setTimeout(fn, 0) callback?

The event loop fully drains the microtask queue before executing a single queued macrotask.

2. What happens to the call stack while an async operation like fetch is pending?

Async work is delegated to the runtime; the call stack stays free until the callback is queued and picked up.

3. What can happen if code inside a microtask keeps scheduling more microtasks?

Since microtasks must fully drain before a macrotask runs, a self-replenishing microtask queue can block macrotasks.

Flash Cards

What is the event loop? โ€” The mechanism that runs queued callbacks once the call stack is empty, prioritizing microtasks over macrotasks.

Microtask examples? โ€” Promise.then callbacks, queueMicrotask, async/await continuations.

Macrotask examples? โ€” setTimeout, setInterval, I/O callbacks, UI events.

Queue drain order per loop tick? โ€” Drain the entire microtask queue, then run exactly one macrotask.

1 / 4

Continue Learning