Microtasks vs Macrotasks
Understanding the difference between microtasks and macrotasks is crucial for predicting async code execution order. This knowledge is essential for debugging timing issues and optimizing performance.
Micro vs Macro
- Microtasks — Promise.then/catch/finally, queueMicrotask(), MutationObserver. Run ALL before rendering
- Macrotasks — setTimeout, setInterval, setImmediate, I/O, UI rendering. Run ONE per event loop cycle
- Rendering — Browser renders BETWEEN macrotasks, AFTER all microtasks. Too many microtasks = rendering delay
- queueMicrotask(fn) — Explicitly add to microtask queue. Runs before setTimeout(fn, 0)
- Starvation — If microtasks keep adding more microtasks, macrotasks never run. Avoid this!
- Practical rule — Use promises/async-await (microtasks) for data flow. Use setTimeout for deferring work
Microtasks vs Macrotasks Code
// Microtasks vs Macrotasks
console.log("Start");
// Macrotask
setTimeout(() => console.log("Timeout 1 (macro)"), 0);
// Microtask
Promise.resolve().then(() => console.log("Promise 1 (micro)"));
// Macrotask
setTimeout(() => console.log("Timeout 2 (macro)"), 0);
// Microtask
Promise.resolve().then(() => {
console.log("Promise 2 (micro)");
// Microtask inside microtask — runs BEFORE macrotasks!
Promise.resolve().then(() => console.log("Promise 3 (nested micro)"));
});
// queueMicrotask — explicit microtask
queueMicrotask(() => console.log("queueMicrotask (micro)"));
console.log("End");
// Output:
// Start
// End
// Promise 1 (micro)
// Promise 2 (micro)
// queueMicrotask (micro)
// Promise 3 (nested micro) ← nested micro runs before ANY macro!
// Timeout 1 (macro)
// Timeout 2 (macro)
// Practical: animation timing
// ❌ Promise micro — runs before browser renders
// Promise.resolve().then(() => element.style.color = "red");
// ✅ requestAnimationFrame — runs before next render
// requestAnimationFrame(() => element.style.color = "red");
console.log("\nMicrotasks always run before macrotasks!");Tip
Tip
Use AbortController to cancel fetch requests when the user navigates away or makes a new request. This prevents race conditions where an old response overwrites a newer one.
Microtasks before macrotasks.
Common Mistake
Warning
Not handling AbortError after aborting a fetch. When you abort, the fetch throws an AbortError. Check: catch(e) { if (e.name !== 'AbortError') { /* real error */ } } to avoid showing false error messages.
Practice Task
Note
Advanced async: (1) Implement a search with debounce that cancels previous requests using AbortController. (2) Create an async generator that yields paginated API results. (3) Build a request queue that processes 3 concurrent requests max.
Quick Quiz
Key Takeaways
- Understanding the difference between microtasks and macrotasks is crucial for predicting async code execution order.
- Microtasks — Promise.then/catch/finally, queueMicrotask(), MutationObserver. Run ALL before rendering
- Macrotasks — setTimeout, setInterval, setImmediate, I/O, UI rendering. Run ONE per event loop cycle
- Rendering — Browser renders BETWEEN macrotasks, AFTER all microtasks. Too many microtasks = rendering delay