setTimeout vs setImmediate vs process.nextTick

setTimeout, setImmediate and process.nextTick are three methods in Node.js that are used to call functions in different phases of the event loop. Sometimes they are used interchangeably, but subtle differences exist between them.

I won’t talk about setInterval, because it falls in the same category as setTimeout with regards to its position in the event loop.

Let’s see then the main features of setTimeout, setTimeout and process.nextTick.

1. setTimeout

Probably this is the method that most developers know and use in their code. It invokes a callback function after a set period of time given in milliseconds.

1.1. Syntax

The signature for setTimeout is the following:

setTimeout(fn, delay)

For example:

setTimeout(()=> {
  console.log('This will run in 5 seconds.')
}, 5000)

In this case, the This will run in 5 seconds. will be displayed in approximately 5 seconds. It’s only approximately, because Node doesn’t guarantee the exact timing of the callback invocation.

If delay is less than 1 or more than 2147483647 (that’s slightly more than 24 855 hours, which is a bit more than 68 years), its value will be set to 1. This built-in feature probably won’t be used very often.

1.2. clearTimeout

setTimeout returns a Timeout object, and as such, it can be assigned a variable:

const timeout = setTimeout(() => {
  // logic comes here
}, 5000)

This is useful, because the timer can be cancelled if the logic of the code requires so:

const timeout = setTimeout(() => {
  // will delete your database entry in 5 seconds
}, 5000)

// later
clearTimeout(timeout)

So when does the logic requires clearTimeout?

One example is when we give the user a chance to cancel an action. If they decide not to do anything within, say, 5 seconds, the callback function will be invoked (in this case, the database entry will be deleted). But, if the user changes their mind, and they still want to keep the entry in the database, they can delete the timer by invoking clearTimeout.

As long as the timer is active (i.e. the callback hasn’t fired yet), the event loop will continue to run.

1.3. setTimeout in the event loop

setTimeout (along with setInterval) runs at the beginning of each event loop cycle (a.k.a. tick). This means that the callback inside setTimeout will fire before any I/O callbacks, like those in fs methods.

2. setImmediate

This method is more rarely used. It fires the callback immediately before the end of the current cycle in the event loop.

2.1. Syntax

It has a simpler syntax than setTimeout because we can’t specify time here as the method is automatically invoked by Node:

setImmediate(() => {
  // cleaning up logic here
})

2.2. clearImmediate

setImmediate returns an Immediate object, which makes it possible to clear it if the need arises:

const immediate = setImmediate(() => {
  // logic here
})

clearImmediate(immediate)

The principle is the same as in the case of setTimeout.

2.3. setImmediate in the event loop

As it was stated above, setImmediate’s callback is invoked at the end of the event loop cycle after every other I/O (like the abovementioned fs module) and OS tasks (like server listening to port).

So timer (setTimeout, setInterval) callbacks are invoked first, followed by any I/O and OS task callbacks, and at the end of the loop cycle comes the callback of setImmediate. Please refer to this post for more practical examples.

3. process.nextTick

process.nextTick doesn’t belong to the list of timers, but because it’s often mistaken with setTimeout, I’ll need to mention it here.

process.nextTick accepts a callback:

process.nextTick(() => {
  console.log('This will be displayed in the next cycle of the event loop.')
})

The callback will be invoked as the very first thing in the next event loop cycle before any other callbacks (timer, I/O etc.). A practical use case of process.nextTick is to set up event handlers before any I/O has occurred.

4. setTimeout(fn, 0) vs process.nextTick

So now it’s clear what the main difference is between setTimeout(fn, 0) and process.nextTick.

setTimeout(fn, 0) is commonly used to call fn asynchronously, i.e. in the next cycle of the event loop. The callback of process.nextTick is placed at the start of the next cycle callback queue, and because it gets called before other callbacks in the next tick, it’s more powerful and gives us more control:

console.log('Start')

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

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

console.log('Finish')

This will return the following output:

Start
Finish
From process.nextTick
From setTimeout

It can be seen that From process.nextTick is logged earlier than From setTimeout, although setTimeout was declared before process.nextTick.

5. Conclusion

Timers in Node are often used, and the difference between them is not only syntactical.

Their callbacks get invoked at different phases on the event loop cycle, and it does matter which one is used and how.

Thanks for reading, and see you next time.