Microtask Queue: Promises & queueMicrotask
MedThe Microtask Queue is a special queue for high-priority asynchronous operations. Promises (.then, .catch, .finally), queueMicrotask(), and MutationObserver use this queue. The critical difference from macrotasks: the event loop drains the ENTIRE microtask queue before running any macrotask. This means microtasks always execute before the next macrotask, even if queued later.
Interactive Visualization
Microtask Queue
(empty)
Macrotask Queue
(empty)
CodeSync
1console.log('1');23setTimeout(() => console.log('timeout'), 0);45Promise.resolve()6 .then(() => console.log('promise'));78console.log('2');
Output
1
Script starts executing synchronously.
1 / 7
Key Insight: Microtasks ALWAYS run before macrotasks. setTimeout(0) means "next macrotask tick", not "immediately"!
Key Points
- Microtasks: Promises, queueMicrotask, MutationObserver
- Event loop drains ALL microtasks before next macrotask
- Microtasks can queue more microtasks (drained recursively)
- Always execute before next macrotask
Code Examples
Promise vs setTimeout Priority
console.log("1"); setTimeout(() => console.log("2"), 0); Promise.resolve().then(() => console.log("3")); console.log("4"); // Output: 1, 4, 3, 2 // Why 3 before 2? // 1. Sync: 1, 4 // 2. Promise queues microtask // 3. setTimeout queues macrotask // 4. Event loop: Run ALL microtasks (3) // 5. Then run one macrotask (2)
Microtasks (Promises) run before macrotasks (setTimeout), regardless of order queued.
Chained Promises Queue Multiple Microtasks
Promise.resolve() .then(() => { console.log("1"); return Promise.resolve("2"); }) .then(v => console.log(v)); setTimeout(() => console.log("3"), 0); console.log("0"); // Output: 0, 1, 2, 3 // Each .then() queues a microtask // All microtasks drain before setTimeout
Each Promise .then() queues a separate microtask. All run before any macrotask.
Microtask Starvation
// DANGER: Infinite microtasks block everything function infiniteMicrotasks() { Promise.resolve().then(() => { console.log("Microtask"); infiniteMicrotasks(); // Queue another }); } // infiniteMicrotasks(); // setTimeout(() => console.log("Never runs"), 0); // The microtask queue never empties // Macrotasks are starved forever
Recursively queueing microtasks prevents macrotasks from ever running.
Common Mistakes
- Thinking Promise.then() is like setTimeout
- Not knowing microtasks run before macrotasks
- Creating infinite microtask loops
Interview Tips
- Know the order: Sync → All Microtasks → One Macrotask
- Explain why Promise is faster than setTimeout
- Know that microtasks can starve macrotasks
- List microtask sources: Promises, queueMicrotask