Microtask vs Macrotask in JavaScript — With Node.js vs Browser Notes
Recap (Browser)
- Microtasks:
Promise.then/catch/finally,queueMicrotask,MutationObserver - Macrotasks:
setTimeout,setInterval,requestAnimationFrame, DOM events - Order per tick: run script → drain all microtasks → possibly render → then the next macrotask.
Node.js Event Loop Differences
Node.js has phases in its event loop (simplified):
- Timers →
setTimeout/setIntervalcallbacks - Pending Callbacks
- Poll → I/O callbacks; may block waiting for I/O
- Check →
setImmediatecallbacks - Close Callbacks
After each phase, Node runs microtask queues in this order:
process.nextTickqueue (✅ runs before promise microtasks)- Promise microtasks (jobs scheduled by
Promise.then/queueMicrotask)
Key takeaways
process.nextTickalways runs before other microtasks and before moving to the next phase. Overuse can starve the loop.setImmediateruns in the Check phase.setTimeout(..., 0)runs in the Timers phase of a later tick.- Order of
setTimeout(…,0)vssetImmediatecan vary:- After I/O,
setImmediatetends to run first (since the loop is already at the Check phase). - Without I/O, timers may run first (implementation- and timing-dependent).
- After I/O,
Example: Node.js ordering
// node-order.js
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise microtask'));
process.nextTick(() => console.log('nextTick microtask'));
Typical Node output:
nextTick microtask
promise microtask
[ either 'immediate' or 'timeout' first, scenario-dependent ]
Why:
process.nextTickruns before other microtasks.- Promise microtasks run next.
- Then the loop continues to a phase where either
setImmediateorsetTimeoutfires first.
Tip: If you place the scheduling inside an I/O callback (e.g.,
fs.readFile),setImmediateis more likely to run beforesetTimeout.
Example: Browser ordering
<script>
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise microtask'));
// process.nextTick / setImmediate are Node-only
</script>
Browser output:
promise microtask
timeout
Quick Matrix
| API / Concept | Browser Microtask | Browser Macrotask | Node Microtask | Node Macrotask |
|---|---|---|---|---|
Promise.then | ✔️ | ❌ | ✔️ | ❌ |
queueMicrotask | ✔️ | ❌ | ✔️ | ❌ |
MutationObserver | ✔️ | ❌ | ❌ (browser only) | ❌ |
setTimeout / setInterval | ❌ | ✔️ | ❌ | ✔️ (Timers) |
requestAnimationFrame | ❌ | ✔️ (render step) | ❌ | ❌ |
setImmediate | ❌ | ❌ | ❌ | ✔️ (Check) |
process.nextTick | ❌ | ❌ | ✔️ (before other microtasks) | ❌ |
Mental Model 🧠
- Browser: script → microtasks (Promises) → macrotask (timeout) → render.
- Node: phase → nextTick → Promise microtasks → next phase;
setImmediatein Check, timers in Timers.
Use process.nextTick sparingly (it’s too eager), and prefer Promise microtasks for normal “run right after” semantics.
Microtask vs Macrotask — Visual Diagram
This diagram shows how a microtask (Promise) runs before a macrotask (setTimeout) after the script finishes.
That’s it — now you can reason about microtasks/macrotasks across both environments with confidence.
How did you like this post?
👍0
❤️0
🔥0
🤔0
😮0