DEV Community

Mark
Mark

Posted on • Originally published at logicmason.com on

setTimeout vs setImmediate vs process.nextTick

What's the difference between setTimeout(callback, 0) and process.nextTick(callback)? How about Node's setImmediate(callback)?

On the surface it appears that all three functions do the same thing—they execute the callback after the current event loop, but before anything else. The natural question to ask is, why are there three different functions? Let's run an experiment:

let racer = function() {
  setTimeout(() => console.log("timeout"), 0);
  setImmediate(() => console.log("immediate"));
  process.nextTick(() => console.log("nextTick"));
  console.log("current event loop");
}

racer()

We can see from the output that these callbacks aren't executed in the same order they were written in the source code.

[Running] node "/Users/logicmason/timeouts.js"
current event loop
nextTick
timeout
immediate

[Done] exited with code=0 in 0.203 seconds

Explanation

The first one executed was process.nextTick, which puts its callback at the front of the event queue. It will execute after the code currently being executed but before any I/O events or timers.

Next is "timeout". Since we passed setTimeout a timeout of 0, there's no additional enforced delay before its execution, and it is placed on into the timer queue during the next loop.

Finally, we have setImmediate, which is clearly not as immediate as its name suggests! Its callback is placed in the check queue of the next cycle of the event loop. Since the check queue occurs later than the timer queue, setImmediate will be slower than setTimeout 0.

All in all, the event loop looks like this:

timers -> IO -> poll -> check ->close -> timers -> ...

Timers: callbacks from setInterval or setTimeout
IO callbacks: callbacks from I/O events
Idle: used internally by Node between IO and Poll phases
Poll: retrieve new I/O events
Check: callbacks from setImmediate execute here
Close: handle closed connections like sockets

Challenge time!

What do you expect the output of the following code in Node to be?

let racer1 = function() {
  setTimeout(() => console.log("timeout"), 0);
  setImmediate(() => console.log("immediate"));
  process.nextTick(() => console.log("nextTick"));
}

let racer2 = function() {
  process.nextTick(() => console.log("nextTick"));
  setTimeout(() => console.log("timeout"), 0);
  setImmediate(() => console.log("immediate"));
}

let racer3 = function() {
  setImmediate(() => console.log("immediate"));
  process.nextTick(() => console.log("nextTick"));
  setTimeout(() => console.log("timeout"), 0);
}

racer1()
racer2()
racer3()

Was it what you expected?

Subscribe to more content from logicmason.com

Discussion (12)

Collapse
anduser96 profile image
Andrei Gatej

Thanks for pointing out the difference between them!

One thing confuses me:

Finally, we have setTimeout. /* ... */, but it does run after I/0 events and after setImmediate.

So if it runs after setImmediate, why is timeout printed before immediate?

Collapse
logicmason profile image
Mark Author • Edited

Clearly my model of how this works is wrong! I did some research in writing this, but the timeout is running first here.

Time to do some more investigation and revise this. Thanks!

Edit: setImmediate callbacks go into the "close" queue! I've updated the post.

Collapse
logicmason profile image
Mark Author

The output is...

[Running] node "/Users/logicmason/timeouts.js"
nextTick
nextTick
nextTick
timeout
timeout
timeout
immediate
immediate
immediate

[Done] exited with code=0 in 0.355 seconds
Collapse
andreasjakof profile image
Andreas Jakof

which of the next ticks will be first? Is it FIFO or LIFO?
According to your previous explanation I‘d say LIFO.

Collapse
sachingoel profile image
Sachin Goel • Edited

No It will be FIFO

Collapse
andreasjakof profile image
Andreas Jakof

So in your example it will be
nextTick (racer1)
nextTick (racer2)
nextTick (racer3)
...?

Thread Thread
sachingoel profile image
Sachin Goel

This is not going to affect the result anyway.

Thread Thread
andreasjakof profile image
Andreas Jakof

Not in this case, but if there are three different callbacks set this way, it might be. That's why I'm asking.
Or are they all called at the same time, if you have enough cores? Then they will be started one after another in FIFO order, but they might end in a different order, even if they are doing exactly the same, because of different workloads on different cores.

As long as they are independent it should not matter ... all hail a functional approach. But if you work on the same object with three different callbacks, this might be a question, that could become very important.

Or is all of this dependent on the browser and its JS implementation?

I clearly (luckily?) don't have enough experience with JS, as I am working mostly in the backend.

Thread Thread
logicmason profile image
Mark Author

I think they're FIFO, but I definitely wouldn't rely upon that behavior.

Re: using multiple cores, in that case you're probably running an instance of Node on each and using PM2.

Collapse
jaishwalrishabh profile image
Rishabh jaishwal

code 1

setTimeout(function cb1() {
console.log("getting")
}, 0);

setImmediate(cb1);

code 2

setImmediate(cb1);
setTimeout(function cb1() {
console.log("getting")
}, 0);


code 3

function cb1() {
console.log("call second");
}
setImmediate(cb1);
setTimeout(function cb1() {
console.log("getting")
}, 0);
process.nextTick(cb);


code 3

function cb1() {
console.log("call second");
}

setTimeout(function cb1() {
console.log("getting")
}, 0);
setImmediate(cb1);
process.nextTick(cb);


SEE UNEXPECTED BEHAVIOUR

Collapse
nash567 profile image
bipen chandra adhikari

is it necessary to write the code inside a callback to be inserted in a event loop

Collapse
iofirag profile image
iofirag

thanks!!!