microtasks vs macrotasks: how do process.nextTick, Promises, setTimeout, and setImmediate order?

3 minadvancednodejsmicrotaskmacrotasknextticksetimmediate

Quick Answer

Macrotasks are event-loop phase callbacks (timers, I/O, setImmediate). Microtasks (process.nextTick and Promise callbacks) run after the current operation and are fully drained before the loop continues. process.nextTick has higher priority than Promise microtasks; setImmediate (check) vs setTimeout(0) (timers) ordering can vary outside an I/O cycle.

Detailed Answer

Answer:

Macrotasks are handled by event-loop phases:

  • setTimeout / setIntervaltimers phase
  • I/O completion → poll phase
  • setImmediatecheck phase

Microtasks run between macrotasks and are drained completely before moving on:

  • process.nextTick callbacks (Node-specific, highest priority)
  • Promise reactions (.then/catch/finally, await continuations)

Priority within a tick: current operation → all process.nextTickall Promise microtasks → next macrotask.

setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
// nextTick, promise, timeout, immediate   (order of first two is guaranteed)

setTimeout(fn, 0) vs setImmediate(fn):

  • At the top level, their order is not guaranteed (depends on how fast the loop reaches the timers phase).
  • Inside an I/O callback, setImmediate always fires before a setTimeout(0), because the check phase comes right after poll:
fs.readFile('f.txt', () => {
  setTimeout(() => console.log('timeout'), 0);
  setImmediate(() => console.log('immediate'));
  // immediate, then timeout — deterministic here
});

Caution with process.nextTick: because it's drained before anything else, recursively scheduling nextTick can starve the event loop (I/O never gets a turn). Prefer setImmediate when you just want to yield.