Understanding process.nextTick()

By

Understand how Node.js process.nextTick() works with the event loop, running your callback at the end of the current operation before the next tick starts.

~~~

As you try to understand the Node.js event loop, one important part of it is process.nextTick().

Every time the event loop takes a full trip, we call it a tick.

When we pass a function to process.nextTick(), we instruct the engine to invoke this function at the end of the current operation, before the next event loop tick starts:

process.nextTick(() => {
  //do something
})

The event loop is busy processing the current function code.

When this operation ends, the JS engine runs all the functions passed to nextTick calls during that operation.

It’s the way we can tell the JS engine to process a function asynchronously (after the current function), but as soon as possible, not queue it.

Let’s see the order. Save this as tick-demo.js and run node tick-demo.js:

console.log('start')

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

setTimeout(() => {
  console.log('timeout')
}, 0)

console.log('end')

You get:

start
end
next tick
timeout

start and end print first because they run now. next tick runs after the current function finishes, before the event loop moves on. timeout waits for a later timers phase.

Calling setTimeout(() => {}, 0) schedules the function for a later timers phase. nextTick() runs sooner: after the current operation, before the event loop moves on. I wrote more about timers in JavaScript timers.

You still see nextTick() in a lot of Node code. For new code, use queueMicrotask().

The Node.js docs mark process.nextTick() as Stability 3 Legacy. That started in Node.js 22, and on the Node.js 20 line too. The note says: use queueMicrotask() instead.

Current Node LTS is Node.js 26.

queueMicrotask(() => {
  //do something
})

queueMicrotask() is the standard way to schedule a microtask. Same idea, no Node-only API.

One ordering detail from the docs: in CommonJS, nextTick callbacks run before queueMicrotask() and promise callbacks. In ES modules, queueMicrotask() runs first.

A common reason to defer work is to keep a callback always async. If you sometimes call it now and sometimes later, the rest of the program has to handle both paths:

function loadSettings(done) {
  const settings = { port: 3000 }

  queueMicrotask(() => {
    done(settings)
  })
}

loadSettings((settings) => {
  console.log(settings.port) //3000
})

loadSettings() returns first, then done runs, so the callback is always async.

Be careful with recursion. If a nextTick callback schedules another nextTick, Node drains that whole queue before timers and I/O. An unbounded loop keeps those from running. Keep nextTick (and queueMicrotask) for a short ordering fix.

Tagged: Node.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about node: