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

Microtask vs Macrotask Queue: What Is the Difference?

Learn the difference between microtasks and macrotasks in the JavaScript event loop, with a clear execution-order example.

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

Expected Interview Answer

The microtask queue holds high-priority callbacks like Promise .then() handlers and queueMicrotask(), which the event loop fully drains after every single synchronous task before it does anything else, while the macrotask queue holds lower-priority callbacks like setTimeout, setInterval, and I/O events, of which only one is processed per event loop tick.

After each macrotask finishes executing (including the very first synchronous script run), the event loop checks the microtask queue and runs every microtask in it, one after another, even if new microtasks are added by earlier ones during that same drain β€” the queue is not considered empty until nothing remains. Only once the microtask queue is completely empty does the event loop move on to render updates (in browsers) and then pull the next single macrotask off its queue. This is why a Promise resolution always runs before a setTimeout(fn, 0), even though both are scheduled 'as soon as possible' β€” the microtask queue has strict priority and is exhaustively emptied between every macrotask. Understanding this ordering is essential for reasoning about async code execution order, since it explains counterintuitive output orderings in interview trick questions mixing synchronous code, Promises, and setTimeout.

  • Guarantees Promise chains resolve before any pending timer or I/O callback runs
  • Keeps related async state updates consistent by fully draining microtasks together
  • Explains why setTimeout(fn, 0) never actually runs immediately, even at 0ms
  • Forms the basis for reasoning correctly about async/await execution order

AI Mentor Explanation

The macrotask queue is like the main over-by-over match schedule, where only one over is bowled per turn before anything else happens. The microtask queue is like urgent DRS review requests that get resolved completely, no matter how many stack up, before the next over is even allowed to start. If a review itself triggers another review, that new one also gets cleared before play resumes. That drain-everything-urgent-before-the-next-scheduled-over rule is exactly how microtasks are prioritized over macrotasks in the event loop.

Step-by-Step Explanation

  1. Step 1

    Run the current synchronous task

    The call stack executes the current script or macrotask callback to completion.

  2. Step 2

    Drain the microtask queue fully

    The event loop runs every queued microtask (Promise callbacks, queueMicrotask) one by one, including any newly added during the drain, until the queue is empty.

  3. Step 3

    Perform rendering (browser only)

    Once microtasks are drained, the browser may perform layout/paint before continuing.

  4. Step 4

    Pull the next single macrotask

    The event loop dequeues exactly one macrotask (e.g. a setTimeout callback) and repeats the whole cycle.

What Interviewer Expects

  • Correct explanation that microtasks fully drain before the next macrotask runs
  • Ability to trace execution order for mixed sync/Promise/setTimeout code
  • Awareness that new microtasks queued during a drain still run before the next macrotask
  • Knowledge of concrete examples: Promise.then/queueMicrotask (micro) vs setTimeout/setInterval (macro)

Common Mistakes

  • Assuming setTimeout(fn, 0) runs before or at the same time as a resolved Promise .then()
  • Believing the microtask queue only runs one task per loop tick like macrotasks do
  • Forgetting that microtasks scheduled during a drain are still processed in that same drain
  • Mixing up which browser APIs are macrotasks (I/O, timers) vs microtasks (Promises, MutationObserver)

Best Answer (HR Friendly)

β€œJavaScript keeps two queues of pending work: a high-priority one for things like resolved Promises, and a lower-priority one for things like setTimeout callbacks. After finishing whatever code is currently running, it completely empties the high-priority queue first, no matter how much is in it, before it even looks at the next item in the lower-priority queue. That is why a Promise callback always beats a setTimeout, even one set to zero milliseconds.”

Code Example

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.then)')
  Promise.resolve().then(() => {
    console.log('3.5: nested microtask, still before setTimeout')
  })
})

console.log('2: sync end')

// Output order:
// 1: sync start
// 2: sync end
// 3: microtask (Promise.then)
// 3.5: nested microtask, still before setTimeout
// 4: macrotask (setTimeout)

Follow-up Questions

  • What happens if a microtask callback keeps scheduling new microtasks indefinitely?
  • Is async/await built on the microtask queue, and if so, how?
  • Which browser APIs run as macrotasks versus microtasks?
  • How does Node.js's event loop phase model relate to microtasks and macrotasks?

MCQ Practice

1. Which runs first: a resolved Promise's .then() callback or a setTimeout(fn, 0) callback?

The microtask queue is fully drained before the event loop proceeds to the next macrotask, so the Promise callback wins.

2. How many macrotasks does the event loop process per loop iteration?

Only a single macrotask is dequeued and run per iteration, unlike microtasks which are fully drained.

3. If a microtask schedules another microtask while the queue is draining, what happens?

The microtask queue is considered fully drained only when it is truly empty, including newly added tasks.

Flash Cards

Microtask queue examples? β€” Promise .then()/.catch()/.finally() callbacks and queueMicrotask().

Macrotask queue examples? β€” setTimeout, setInterval, and I/O/UI event callbacks.

Order between them? β€” All microtasks fully drain before the next single macrotask runs.

Why does setTimeout(fn, 0) not run β€œimmediately”? β€” It is still a macrotask, so it waits for the microtask queue to fully empty first.

1 / 4

Continue Learning