nodejs.dev
nodejs.dev copied to clipboard
setImmediate and setTimeout(callback, 0) running on the next-iteration of the event-loop ?
Summary
On this documentation https://nodejs.dev/learn/understanding-setimmediate, it states that "A setTimeout() callback with a 0ms delay is very similar to setImmediate(). The execution order will depend on various factors, but they will be both run in the next iteration of the event loop."
setImmediate(() => console.log("immediate from main module"));
console.log("Poll Phase Main `Module");
If I execute the code above, I understand that this will be executed in the poll phase, and then it will move to the check phase to execute the queued setImmediate callback. And all of this is supposedly happening in the current iteration of the event loop.
But why does the documentation state that this is on the next iteration???
I also understand that if in case the poll phase wraps back to the timers phase, then I can assume that the following check phase will be executed in the next iteration with the assumption that the timer phase is the start of every new iteration.
Not necessarily. First the event loop starts registering callbacks into their respective queues. Then it executes them looping through its phases: timers -> pc -> idle -> poll -> check -> close
A great explanation of this process: https://dev.to/lunaticmonk/understanding-the-node-js-event-loop-phases-and-how-it-executes-the-javascript-code-1j9
Can you officially confirm this? This seems like an "understanding" documentation. Can you point out where this happens in the Libuv or Node.js internal code base ?
You can simply try it yourself. Look at this:
const immediateRecursive = () => setImmediate(() => {
console.log("immediate");
immediateRecursive();
});
immediateRecursive(); // setInterval will run
console.log("I run first")
const nextTickRecursive = () => process.nextTick(() => {
console.log("nextTick");
nextTickRecursive();
});
nextTickRecursive();
setInterval(() => console.log('\033[1;36minterval\033[0;0m'), 10);
By executing this script, the event loop will be blocked by the recursive next tick, because it gets executed on the current iteration leading to a process called I/O starvation.
Output:
The setImmediate and the setInterval won't never be called.
Now look at this:
const immediateRecursive = () => setImmediate(() => {
console.log("immediate");
immediateRecursive();
});
immediateRecursive(); // setInterval will run
console.log("I run first")
setInterval(() => console.log('\033[1;36minterval\033[0;0m'), 10);
Output:
The interval got called after 10ms. On each iteration the recursive callback is put onto the next check phase. When interval timer expires and the iteration hits the timers phase it executes the setInterval callback.
Try take a look at this if you want some source code: https://developpaper.com/viewing-nodejs-event-loop-from-libuv/ I don't know if it's what you're searching 💯
I think you do not understand my question completely. I am aware of the event loop phases, but the questions are the following:
-
Does the event loop start after main module execution or during it ?? Some sources say that main-module execution happens in the poll-phase (https://heynode.com/tutorial/how-event-loop-works-nodejs/), if that is correct then the check phase should come right after it meaning setImmediates should get executed right after poll-phase and not-timers.
-
setImmediate are said to be executed on the "next iteration", but in an I/O Callback they are executed always first before all the timers, because right after i/o -> poll phase -> check phase. So ideally setImmediate is getting executed on the same iteration and not the next iteration. If this is true, why does the official doc say that the set immediate are executed on the next iteration?
Based on the code I ran and observed, the documentation doesn't really explain it clearly. But here it goes:
setImmediate runs on the next tick of the event loop in the main module. Eg: The initial code from index.js/main.js.
// Both of the codes will be executed in the next tick of the event loop.
// Depending on the event-loop start time, if the timer is executed in the next tick, then setImmediate will also do the same, as the check-phase follows the timers phase.
// If the timer is delayed further then only setImmediate will be executed in the next tick and the timer a tick after that.
setTimeout(() => console.log('May be I run first'), 0);
setImmediate(() => console.log('May be I run first'));
setImmediate runs on the same iteration of the event loop inside an I/O callback. Because timer callbacks are always executed after setImmediate callbacks.
fs.read('/file/path', () => {
// check phase follows the I/O phase and poll phase.
// And when the poll phase is idle then the check phase is run right after.
setImmediate(() => console.log('I am printed first always'));
// Timers will be deferred to the next tick as timers-phase are the start of a "tick".
setTimeout(() => console.log('I will be called in the next tick'), 0);
setTimeout(() => console.log('I will be called may be tick after next one'), 100);
});
@rajeshbabu-oviva Hello, I'm confused about tick
, phase
and one/an loop
. Could you explain this three? Thank you.
@rajeshbabu-oviva Hello, I'm confused about
tick
,phase
andone/an loop
. Could you explain this three? Thank you.
The event loop is a mechanism that allows Node to perform non blocking I/O operations. Phases are the building blocks that compose the event loop. They are:
- Timers
- Pending callbacks
- Idle
- Poll
- Check
- Close callbacks How they work is explained nicely here
Ticks are not part of the libuv library (the one that exposes the event loop), they were implemented in Node. According to nodejs.dev docs a tick is a complete iteration of the 6 phases.
Finally you can deepen the argument on here
@thomscoder Your explanation is informative. Thank you.
@rajeshbabu-oviva think as setImmediate(callback)
as the equivalent of setTimeout(callback, 0)
on EcmaScript.
Do you still believe documentation changes are required?
Closing because this page is going away. https://github.com/nodejs/nodejs.dev/pull/2796