setTimeout vs setImmediate vs process.nextTick
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.