process.nextTick vs setImmediate in Node.js

While both process.nextTick() and setImmediate() are used to schedule asynchronous tasks in Node.js, their core difference lies in when their callbacks are executed within the Node.js event loop. process.nextTick() fires immediately after the current operation completes, before the event loop advances to the next phase. In contrast, setImmediate() places callbacks into the event loop’s Check phase, ensuring they run on the subsequent iteration after pending I/O events.


What is process.nextTick()?

process.nextTick() is technically not part of the event loop phases. Instead, it processes callbacks in the microtask queue (specifically the nextTickQueue).

process.nextTick(() => {
  console.log("Executed in process.nextTick");
});

What is setImmediate()?

setImmediate() is designed to execute code in the Check phase of the event loop.

setImmediate(() => {
  console.log("Executed in setImmediate");
});

Execution Order Example

Consider the following script:

const fs = require('fs');

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('setTimeout');
  }, 0);

  setImmediate(() => {
    console.log('setImmediate');
  });

  process.nextTick(() => {
    console.log('process.nextTick');
  });
});

Output:

process.nextTick
setImmediate
setTimeout

Explanation of Output: 1. Once the file read (I/O) completes, the callback runs. 2. process.nextTick() resolves immediately before transitioning to the next event loop phase. 3. The event loop transitions from the Poll phase directly into the Check phase, triggering setImmediate(). 4. setTimeout() executes in the Timers phase on the subsequent loop iteration.


Key Differences at a Glance

Feature process.nextTick() setImmediate()
Event Loop Phase Microtask queue (runs before the next phase). Check phase of the event loop.
Priority Higher priority; runs before setImmediate. Lower priority; queued within the loop.
Starvation Risk High if called recursively. Low; allows the loop to continue.
Primary Use Case Cleaning up resources, handling errors before I/O, or ensuring APIs remain consistently asynchronous. Executing non-blocking tasks after the current I/O cycle completes.

When to Use Which?