Skip to solution
hardDSA

What is `process.nextTick()` and `setImmediate()` and when to use them?

1.2k views
01

Understand the problem

Question presented to candidate: "Both process.nextTick() and setImmediate() schedule a callback to run 'soon' rather than synchronously. What is actually different about them, and can you prove it rather than just state it?"

What a strong answer should cover:

  • process.nextTick(fn) schedules fn onto a microtask queue that is drained completely, including anything it recursively schedules, before the event loop proceeds to its next phase. It runs before Promise microtasks, and before any timer or I/O callback.
  • setImmediate(fn) schedules fn to run in the check phase — a real, distinct stop the event loop makes on every lap, after the poll (I/O) phase.
  • 📌 The verifiable, not just definitional, distinction: because nextTick is a microtask queue, an unbounded recursive process.nextTick() call can starve the event loop entirely — I/O callbacks never get a turn, because the loop never advances past the microtask-draining step. The identical recursive pattern using setImmediate() cannot do this, because check is a real phase that only runs once per lap, always preceded by a poll-phase visit.
  • Relative to setTimeout(fn, 0): inside an I/O callback, setImmediate is guaranteed to run before a setTimeout(fn, 0) scheduled at the same point, because poll transitions to check before looping back to timers. At the top level of a script, the order between the two is not guaranteed and depends on process startup timing.
  • Practical uses: process.nextTick() is for guaranteeing a callback runs before the event loop continues at all — commonly, ensuring an API always calls its callback asynchronously (even when the result is already available) so callers can rely on consistent, never-synchronous behavior. setImmediate() is for deferring work to after the current poll phase, to avoid hogging I/O processing — a common choice for breaking up a large synchronous chunk of work into smaller pieces that let I/O interleave.
  • A good answer explicitly avoids over-generalizing "nextTick runs first" into "nextTick is always what you want" — the starvation risk above is a real reason to prefer setImmediate for recursive/repeated scheduling.

Clarifying questions expected:

  • "Is this about deferring one callback, or about something that recurses/repeats?" — the starvation risk only matters for the latter.
  • "Does the use case need to run before the event loop continues at all, or just after I/O has had a turn?" — this is exactly the nextTick-vs-setImmediate decision.

Code / implementation expected: Yes — the starvation test (recursive nextTick vs recursive setImmediate, racing a real I/O callback) is the single most convincing, concrete demonstration of the distinction.

event loopschedulingasynchronousperformance
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js interviews — assumes basic event-loop familiarity. Difficulty: Hard

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The starvation test below was actually executed on Node v24.19.0

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

The starvation test: recursive process.nextTick() vs recursive setImmediate(), racing a real fs.readFile callback
const fs = require("node:fs");

// mode = "nexttick" or "setimmediate"
const mode = process.argv[2];
let count = 0;
const MAX = 200000;
let ioFired = false;

fs.readFile(__filename, () => {
  ioFired = true;
  console.log(`[${mode}] I/O callback fired after ${count} recursive calls`);
});

function recurse() {
  count++;
  if (count >= MAX) {
    console.log(`[${mode}] stopped after hitting the cap — ioFired=${ioFired}`);
    process.exit(0);
  }
  if (mode === "nexttick") process.nextTick(recurse);
  else setImmediate(recurse);
}
recurse();

// node script.js nexttick      -> ioFired=false, cap reached, I/O NEVER ran
// node script.js setimmediate  -> "I/O callback fired after 6 recursive calls"
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 114 of 152 decoded in the Node.js track. One more won't hurt.

Back to track