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
Top comments (15)
Thanks for pointing out the difference between them!
One thing confuses me:
So if it runs after
setImmediate
, why istimeout
printed beforeimmediate
?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.The output is...
which of the next ticks will be first? Is it FIFO or LIFO?
According to your previous explanation I‘d say LIFO.
No It will be FIFO
So in your example it will be
nextTick (racer1)
nextTick (racer2)
nextTick (racer3)
...?
This is not going to affect the result anyway.
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.
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.
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
Not sure I am understanding your explanation of setImmediate rightly.
Consider following code. setImmediate will be executed before the timer.
const fs = require('fs');
fs.readFile('raw.githubusercontent.com/komljen/... => {
racer();
});
let racer = function() {
setTimeout(() => console.log("timeout"), 0);
setImmediate(() => console.log("immediate"));
process.nextTick(() => console.log("nextTick"));
console.log("current event loop");
}
nextTick
nextTick
nextTick
timeout
timeout
timeout
immediate
immediate
immediate
//this what i get from the out put, but i thought like this
nextTick
timeout
immediate
nextTick
timeout
immediate
nextTick
timeout
immediate
is it necessary to write the code inside a callback to be inserted in a event loop
I think I have understood the difference when I read in Node.js doc :
nodejs.dev/learn/understanding-set...
The explanation is clear there.
thanks!!!