Skip to content

Node.js process.nextTick — Complete Guide to Microtask Scheduling

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Node.js Process.nextTick. We cover key concepts, practical examples, and best practices to help you master this topic.

Node.js process.nextTick schedules a callback to run at the end of the current operation, before the next event loop phase begins, making it the highest priority microtask.

What You'll Learn

By the end of this tutorial, you'll use process.nextTick to defer callbacks, understand its event loop position, avoid starvation, and choose between nextTick, setImmediate, and Promise microtasks.

Why nextTick Matters

nextTick guarantees execution before I/O, timers, and setImmediate. It is essential for error cleanup, async initialization, and ensuring callbacks run after the current synchronous block.

Real-World Use

A database connection module uses nextTick to emit a "connected" event after construction but before any I/O, ensuring event handlers registered before the next event loop iteration fire immediately.

nextTick Learning Path

flowchart LR
  A[Timers] --> B[nextTick]
  B --> C[Blocking vs Non-Blocking]
  C --> D[Async Patterns]
  D --> E[Error Handling]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Basic nextTick Usage

process.nextTick registers a callback that runs after the current operation completes but before the event loop continues to the next phase.

console.log("Start");
process.nextTick(() => {
  console.log("nextTick: runs before any I/O or timer");
});
console.log("End");
// Output:
// Start
// End
// nextTick: runs before any I/O or timer

nextTick in the Event Loop

nextTick callbacks execute between phases, not within them. If a timer callback runs, nextTick callbacks registered inside it run before moving to the next phase.

const fs = require("node:fs");
fs.readFile(__filename, "utf8", () => {
  console.log("1. I/O callback (poll phase)");
  process.nextTick(() => console.log("2. nextTick after I/O"));
  setImmediate(() => console.log("3. setImmediate check phase"));
  process.nextTick(() => console.log("4. another nextTick"));
});
// Output:
// 1. I/O callback (poll phase)
// 2. nextTick after I/O
// 4. another nextTick
// 3. setImmediate check phase

Recursive nextTick Starvation

Calling process.nextTick recursively blocks I/O and timers from ever running. The event loop never reaches subsequent phases.

function recursiveNextTick() {
  process.nextTick(() => {
    console.log("Recursive nextTick - starving the loop");
    recursiveNextTick();
  });
}
// WARNING: This will prevent I/O, timers, and setImmediate from ever executing
// To avoid, use setImmediate for deferring work

nextTick vs setImmediate

nextTick runs before setImmediate. Use nextTick for operations that must execute before I/O continues. Use setImmediate for deferring work without starving I/O.

const fs = require("node:fs");
fs.readFile(__filename, "utf8", () => {
  process.nextTick(() => console.log("nextTick: I/O waits for me"));
  setImmediate(() => console.log("setImmediate: I/O already done"));
});
// nextTick: I/O waits for me
// setImmediate: I/O already done

nextTick Error Handling

process.nextTick is the only place where uncaught exceptions can be caught with a domain (deprecated) or by wrapping in try-catch inside the callback.

try {
  process.nextTick(() => {
    throw new Error("This crashes the process");
  });
} catch (e) {
  console.log("This never catches the error");
}
// The error is thrown outside the try-catch scope

Common Mistakes

1. Using nextTick for CPU-Intensive Work

nextTick does not offload work to another thread. It still runs on the main thread. Use worker threads for CPU work.

2. Recursive nextTick Causing Starvation

Each nextTick callback that schedules another nextTick prevents I/O and timers from running indefinitely.

3. Assuming nextTick Runs Before All Other Callbacks

Promise.then microtasks also run between phases. The order is: Promise microtasks first, then nextTick callbacks.

4. Using nextTick When setImmediate Is Safer

setImmediate limits its queue and allows I/O to proceed. Use setImmediate for deferring work unless you specifically need phase barrier.

5. Forgetting nextTick in Constructor Pattern

Some APIs use nextTick to defer synchronous side effects. If you expect immediate behavior, this can cause bugs.

Practice Questions

1. When does process.nextTick execute?

After the current operation completes but before the next event loop phase begins.

2. What order do microtasks execute?

Promise.then callbacks run before process.nextTick callbacks within the same phase transition.

3. What happens if you recursively call process.nextTick?

It prevents I/O, timers, and setImmediate from ever executing. The event loop is starved.

4. When should you use setImmediate instead of nextTick?

When you want to defer work without starving I/O. setImmediate allows the poll phase to run.

5. Challenge: Create a safe deferred execution function that limits nextTick depth.

function safeDefer(fn, maxDepth = 1000) {
  let depth = 0;
  function runner() {
    if (depth < maxDepth) {
      depth++;
      process.nextTick(runner);
    } else {
      setImmediate(fn);
    }
  }
  runner();
}
safeDefer(() => console.log("Finally executed"));

FAQ

What is the difference between process.nextTick and Promise.then?

Promise.then microtasks run before process.nextTick. Both run between phases.

Can I use process.nextTick in browser JavaScript?

No. process.nextTick is Node.js specific. Browsers use queueMicrotask() or Promise.resolve().then().

Does process.nextTick affect performance?

Excessive nextTick calls slow the event loop. Each call adds overhead. Use sparingly.

Why does Node.js have nextTick and setImmediate?

Historical naming. nextTick runs before setImmediate despite the name suggesting the opposite. Do not rename mentally.

Is process.nextTick still recommended?

Yes, for specific use cases like error cleanup, async initialization, and guaranteeing execution order. Avoid for general deferral.

Mini Project: Async Initializer with nextTick

Build a module that initializes asynchronously and signals completion via nextTick.

class DatabaseConnection {
  constructor(config) {
    this.connected = false;
    this.config = config;
    this.queue = [];
    process.nextTick(() => this.initialize());
  }
  initialize() {
    console.log(`Connecting to ${this.config.host}...`);
    this.connected = true;
    this.queue.forEach((cb) => cb());
    this.queue = [];
  }
  query(sql, callback) {
    if (this.connected) {
      callback(`Result for: ${sql}`);
    } else {
      this.queue.push(() => callback(`Result for: ${sql}`));
    }
  }
}
const db = new DatabaseConnection({ host: "localhost" });
db.query("SELECT 1", (result) => console.log(result));

What's Next

Node.js Blocking vs Non-Blocking Node.js Async Patterns Node.js Error Handling

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro