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
/setInterval
callbacks - Pending Callbacks
- Poll → I/O callbacks; may block waiting for I/O
- Check →
setImmediate
callbacks - Close Callbacks
After each phase, Node runs microtask queues in this order:
process.nextTick
queue (✅ runs before promise microtasks)- Promise microtasks (jobs scheduled by
Promise.then
/queueMicrotask
)
Key takeaways
process.nextTick
always runs before other microtasks and before moving to the next phase. Overuse can starve the loop.setImmediate
runs in the Check phase.setTimeout(..., 0)
runs in the Timers phase of a later tick.- Order of
setTimeout(…,0)
vssetImmediate
can vary:- After I/O,
setImmediate
tends 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.nextTick
runs before other microtasks.- Promise microtasks run next.
- Then the loop continues to a phase where either
setImmediate
orsetTimeout
fires first.
Tip: If you place the scheduling inside an I/O callback (e.g.,
fs.readFile
),setImmediate
is 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;
setImmediate
in 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