setImmediate()

Schedules a callback to run on the check phase, after the current I/O callbacks complete.

Signature
setImmediate(callback[, ...args])
  • Added in Node.js v0.9.1

Parameters

ParameterTypeDescription
$callback<Function>The function to call at the end of this turn of the Node.js Event Loop.
$...argsoptional<any>Optional arguments to pass when the callback is called.

Return value

<Immediate>An Immediate object that can be passed to clearImmediate() to cancel the scheduled callback.

Errors

Throws ERR_INVALID_ARG_TYPE if callback is not a function.

Changelog

  • PHP v18.0.0Passing an invalid callback to the callback argument now throws ERR_INVALID_ARG_TYPE instead of ERR_INVALID_CALLBACK.

setImmediate() queues a callback for the event loop's check phase, which runs immediately after the poll (I/O) phase of the current iteration. It is not a timer and takes no delay.

When to reach for it

  • Yielding between chunks of a long CPU-bound job so I/O and timers are not starved.
  • Deferring work from inside an I/O callback until the current batch of I/O has been handled.
  • Letting a caller attach listeners before you emit an event from a constructor.

What trips people up

The famous comparison with setTimeout(fn, 0) has two different answers. Called from the main module, the order is non-deterministic — it depends on how long process startup took relative to the 1 ms timer clamp, so the same script can print either order on different runs. Called from inside an I/O callback, setImmediate() always wins, because the loop is already past the timers phase and reaches check before wrapping around. Only rely on ordering in the second case.

process.nextTick() is not the same thing despite the names suggesting otherwise: it drains before the loop continues at all, so a recursive nextTick can starve I/O entirely. setImmediate() cannot.

An Immediate is ref'd and keeps the process alive; cancel it with clearImmediate().

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Chunk a large array transform so the server keeps answering requests
  • Defer a follow-up write until the current read callback has returned
  • Emit an event after the constructor returns so listeners can attach
  • Break a recursive algorithm into event-loop-friendly steps

Frequently asked questions

What is the difference between setImmediate and setTimeout(fn, 0)?

setImmediate() runs on the check phase of the current loop iteration, while setTimeout(fn, 0) is clamped to 1 ms and runs on the next timers phase. Inside an I/O callback setImmediate always fires first; from the main module the order is non-deterministic.

Is setImmediate the same as process.nextTick?

No — despite the names, process.nextTick() drains before the event loop continues, whereas setImmediate() waits for the check phase. Recursive nextTick calls can starve I/O entirely; recursive setImmediate calls cannot, which makes setImmediate the right tool for yielding.

How do I cancel a setImmediate?

Pass the returned Immediate object to clearImmediate(). It is also worth knowing that a pending Immediate keeps the process alive, so call immediate.unref() (added in v9.7.0) if it should not.

More examples

Chunking CPU work so the loop keeps breathing

The loop processes 1000 items, then hands control back to the event loop before continuing. A plain for-loop over 250,000 items blocks every incoming request until it finishes; this version keeps the server responsive at the cost of a slightly longer total runtime.

Example · javascript
function mapInChunks(items, fn, chunkSize = 1000) {
  return new Promise((resolve) => {
    const out = [];
    let i = 0;

    function step() {
      const end = Math.min(i + chunkSize, items.length);
      for (; i < end; i++) out.push(fn(items[i]));

      if (i < items.length) {
        setImmediate(step);      // yield: let I/O and timers run
      } else {
        resolve(out);
      }
    }

    step();
  });
}

const big = Array.from({ length: 250_000 }, (_, n) => n);
const squares = await mapInChunks(big, (n) => n * n);
console.log('chunks processed, last value:', squares.at(-1));

Deferring an emit so listeners can attach

Prints 'listener attached' first, then 'ready: config'. Without setImmediate the emit would happen inside the constructor, before the .on() call on the next line exists, and the event would be silently missed.

Example · javascript
const { EventEmitter } = require('node:events');

class Loader extends EventEmitter {
  constructor(name) {
    super();
    this.name = name;
    // Emitting synchronously here would fire before the caller
    // has had a chance to call .on('ready', ...).
    setImmediate(() => this.emit('ready', this.name));
  }
}

const loader = new Loader('config');
loader.on('ready', (name) => console.log('ready:', name));
console.log('listener attached');

setImmediate versus process.nextTick

The nextTick queue and promise microtasks drain before the event loop advances at all, so they run ahead of any setImmediate. That is why a recursive process.nextTick() can starve I/O completely while a recursive setImmediate() cannot — the loop still gets a turn between each one.

Example · javascript
setImmediate(() => console.log('C: setImmediate (check phase)'));
process.nextTick(() => console.log('B: nextTick (before the loop continues)'));
Promise.resolve().then(() => console.log('B2: microtask'));
console.log('A: synchronous');

// A: synchronous
// B: nextTick (before the loop continues)
// B2: microtask
// C: setImmediate (check phase)

Discussion

  • Be the first to comment on this lesson.