As per my understanding the setImmediate is executed in the Check phase of the event loop. The Poll phase schedules the timers to be executed in the Timers phase if their minimum threshold is elapsed.
But the following code executes the timer callback before the setImmediate one (not always). How is that possible since the timer should be scheduled in the Poll phase and the Check phase comes after it ?
Is it something related to that code is executed in the main module, if so how come setImmediate sometimes executed before the timer ?
setTimeout(function() {
console.log('setTimeout');
}, 0);
setImmediate(function() {
console.log('setImmediate');
});
Related
In the official docs, "setImmediate() vs setTimeout()" section, it says:
"setImmediate() is designed to execute a script once the current poll phase completes", and as i understand, it means also current tick/event loop cycle.
However later in the "process.nextTick() vs setImmediate()" section, it says:
"setImmediate() fires on the following iteration or 'tick' of the event loop".
So which one is the correct answer, did i miss anything here?
Thanks in advance.
Docs relevant page: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
As you can see by the image below:
The diagram is from Event Loop and the Big Picture — NodeJS Event Loop Part 1 By Deepal Jayasekara
"The boxes around the circle illustrate the queues of the different phases we seen before, and the two boxes in the middle illustrate two special queues that need to be exhausted before moving from one phase to the other. The “next tick queue” processes callbacks registered via nextTick(), ..."
From Synk - Node.js Event-Loop: How even quick Node.js async functions can block the Event-Loop, starve I/O which reference the article above
So the difference between setTimeout, setImmediate and nextTick is:
setTimeout schedules a script to be run after a minimum threshold in ms has elapsed.1
setImmediate runs after the poll phase (the pool phase is the IO events queue in the diagram)
nextTick runs between phases.
1 The order in which the timers are executed will vary depending on the context in which they are called., see here for more
Simple answer:
setImmediate runs on the next tick.
Details:
setImmediate and setTimeout both run in different phases on the event loop.
That is why when setTimeout is set with zero delay it will not run immediately. Use setImmediate which always run on the next tick of the event loop.
but
process.nextTick basically does not care about phases of event loop. The callback you assign to this method will get executed after the current operation is complete and before event loop continues.
The document you linked has a very good explanation about the differences. I'll just grab your attention to few points here.
In setimmediate-vs-settimeout the order of call is nondeterministic UNLESS they are scheduled within an I/O cycle like below example.
Now add the process.nextTick to the example
const fs = require('fs');
// I/O cycle
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
process.nextTick(() => {
console.log('nextTick')
})
Output:
nextTick
immediate
timeout
If you run the above code whatever is in process.nextTick function will get executed first. The reason is, it runs before event loop continues. Remember, both setTimeout and setImmediate are event loop related but not process.nextTick.
Be very careful how you use the process.nextTick. You can end up in bad situations if you are not using it properly.
The concept of tick is IMO out of scope for us as JS engineers, as there is no native API for us to find or count which tick we are in right now. Sometimes the poll phase waits for the incoming requests to be completed and the event loop stays in the same tick. However, we can indeed try and make sense of the concept with proper code examples.
I asked the same question on the official node.js dev doc and they haven't gotten back to me yet https://github.com/nodejs/nodejs.dev/issues/2343.
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);
});
According to the Node.js DOCs,
when the event-loop enters its poll phase and the poll queue is not empty,
the callbacks in the poll queue should get executed before the event-loop
proceeds further to its check phase.
In reality, however, the opposite happens, that is if neither the poll, nor the
check (setImmediate) queue is empty by the time the event-loop is entering
the poll phase, the callbacks from the check (setImmediate) queue always execute
before the callbacks from the poll queue.
Why is that? What am I missing in the Node.js DOCs?
Here follow the sample piece of code and the quotation from the Node.js DOCs.
The quote from the Node.js DOCs:
The poll phase has two main functions:
1. Calculating how long it should block and poll for I/O, then
2. Processing events in the poll queue.
When the event loop enters the poll phase and there are no timers scheduled,
one of two things will happen:
(a) - If the poll queue is not empty, the event loop will iterate
through its queue of callbacks executing them synchronously
until either the queue has been exhausted,
or the system-dependent hard limit is reached.
(b) - If the poll queue is empty, one of two more things will happen:
- If scripts have been scheduled by setImmediate(),
the event loop will end the poll phase and continue to the check phase
to execute those scheduled scripts.
- If scripts have not been scheduled by setImmediate(),
the event loop will wait for callbacks to be added to the queue,
then execute them immediately.
Once the poll queue is empty the event loop will check for timers
whose time thresholds have been reached. If one or more timers are ready,
the event loop will wrap back to the timers phase
to execute those timers' callbacks.
The sample code:
const fs = require(`fs`);
console.log(`START`);
const readFileCallback = () => {
console.log(`readFileCallback`);
};
fs.readFile(__filename, readFileCallback);
const setImmediateCallback = () => {
console.log(`setImmediateCallback`);
};
setImmediate(setImmediateCallback);
// Now follows the loop long enough to give the fs.readFile enough time
// to finish its job and to place its callback (the readFileCallback)
// into the event-loop's poll phase queue before the "main" synchronous part
// of the this code finishes.
for (let i = 1; i <= 10000000000; i++) {}
console.log(`END`);
// So when the event-loop starts its first tick there should be two callbacks
// waiting:
// (1) the readFileCallback (in the poll queue)
// (2) the setImmediateCallback (in the check queue)
// Now according to the Node.js DOCs, of these two waiting callbacks
// the readFileCallback should execute first, but the opposite
// is actually happening, that is setImmediateCallback executes first.
for(var i = 0; i < 100000; i++){
setTimeout(function(){
console.log("Inside")
}, 0);
console.log("Outside")
};
It gives the output:
Outside * 100000
Inside * 100000
why this output?
CPU bound activities are handled in-line, and when all CPU bound activities are over then it processes the event que?
setTimeout() is non-blocking and it calls it's callback asynchronously. That means that setTimeout() runs, schedules a timer for the future and then immediately returns. Then, sometime later, when the timer fires, it inserts an event in the event queue. When the interpreter is done executing other Javascript, it fetches the next event out of the event queue and will then run the timer callback.
So, here's what happens in your code:
Run the body of the for loop 100,000 times and each time through the loop, schedule a timer and then output console.log("Outside").
Then, when the for loop is done and control returns back to the system, check the event queue to see if there is an event.
Because all the timers were set with a timer of 0, there are probably a lot of timer events in the queue. Pull the first event out and runs it's callback which will output console.log("Inside").
Repeat until there are no more events in the event queue (which will take 100,000 iterations to process all the timers you previously set).
There are two keys here:
setTimeout() is "non-blocking". It schedules the timer and immediately returns.
When the timer fires, it inserts an event in the event queue to run the timer callback and that callback will ONLY get called when the JS interpreter is done doing what else it was doing and can then fetch the next event from the event queue.
This means that all synchronous code finishes first before any asynchronous callbacks can be fetched from the event queue and run.
setTimeout(function(){
console.log('hari');
process.exit()
}, 300);
for(i=0;i<3000000;i++) {
console.log(i);
}
Please explain why setTimeout is not finished in 300 ms here. setTimeout is executing only after the for loop completes; why?
As you might already know, Node.js is using the V8 (JavaScript) engine which is single threaded. The core of I/O execution is around an event-loop; when you block the event loop it also blocks execution of other events.
Basically, the for loop is executed before, and it starts blocking the event loop. The setTimeout does not guarantee that your code gets executed exactly after 300 ms, but more like >= 300 ms. To get a better understanding of event loop read this.
Basically, your code runs in a single thread. setTimeout has lower priority than your standard code, so it will execute first and then (because the thread will be finished) it will execute your function from setTimeout.
setTimeout(milliseconds) runs a function no faster than in the specified number of milliseconds.
the nodejs document say
Schedules "immediate" execution of callback after I/O events' callbacks and before timers set by setTimeout and setInterval are triggered. Returns an immediateObject for possible use with clearImmediate.
but I write a test code as follows:
server = http.createServer((req, res)->
res.end()
)
setImmediate(()->
console.log 'setImmediate'
)
setTimeout(()->
console.log 'setTimeout'
, 0)
process.nextTick(()->
console.log 'nextTick'
)
server.listen(8280, ()->
console.log 'i/o event'
)
why the setTimeout always output befeore setImmediate
SetTimeOut - This type of function will be call after set time which is 0 in your case but it follows event loop. And event loop does not grantee that it will work after 0 seconds. In fact it only guarantees that function will be called after completing set time.
But, function can be called any time after completing time when node event queue is free to take up callback function
Source to understand event loop - https://www.youtube.com/watch?v=8aGhZQkoFbQ
SetImmediate - This will get called as and when it goes to stack and does not follow cycle of callback in event loop.
The main advantage to using setImmediate() over setTimeout() is setImmediate() will always be executed before any timers if scheduled within an I/O cycle, independently of how many timers are present.
However, if executed in the main module, the execution order will be nondeterministic and dependent on the performance of the process
see https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/ for more information