-
Notifications
You must be signed in to change notification settings - Fork 299
Description
- Node.js Version: Any
- OS: Any
- Scope: documentation
The official event loop documentation is here but it has a lot of unclear parts.
The first one is this:
poll: retrieve new I/O events; node will block here when appropriate
This is unclear because:
- it uses the word "retrieve" (events), while all other phase descriptions use the word "execute" (callbacks)
- it refers to I/O, which is confusing since there is already an "I/O callbacks" phase
- what does exactly mean "node will block here when appropriate"?
So... let's move to the paragraphs where the "I/O callbacks" and "poll" phases are explained in detail.
I/O callbacks phase:
This phase executes callbacks for some system operations such as types of TCP errors. For example if a TCP socket receives ECONNREFUSED when attempting to connect, some *nix systems want to wait to report the error. This will be queued to execute in the I/O callbacks phase.
What?! Some paragraphs above it was said that:
I/O callbacks: executes almost all callbacks with the exception of close callbacks, the ones scheduled by timers, and
setImmediate()
This sounds very different.
Anyways... let's move to the poll phase detailed explanation.
It starts with:
The poll phase has two main functions:
- Executing scripts for timers whose threshold has elapsed, then
- Processing events in the poll queue.
Again: what?!
It seems to me that point 1 is a repetition of the "timer" phase, while point 2 is a repetition of the "I/O callbacks" phase.
Note: The diagram also suggests that the poll phase relates to input only, while other parts of the article suggest that the poll phase relates both to input and to output.
Anyways, let's proceed with the "poll" phase detailed description:
When the event loop enters the poll phase and there are no timers scheduled, one of two things will happen:
- 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.
- 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.
Questions:
- What happens when the event loop enters the poll phase and there are timers scheduled?
- What happens when the system-dependent hard limit is reached?
- It seems that callbacks for timers whose thresholds have been reached are executed after emptying the poll queue. But this was previously previously presented as function number 1 (of 2) of the poll phase, so I ask myself if this kind of callbacks is executed only at the end of the poll phase or both at the beginning and at the end.
The timer phase detailed explanation provides an interesting example:
For example, say you schedule a timeout to execute after a 100 ms threshold, then your script starts asynchronously reading a file which takes 95 ms.
When the event loop enters the poll phase, it has an empty queue (
fs.readFile()
has not completed), so it will wait for the number of ms remaining until the soonest timer's threshold is reached. While it is waiting 95 ms pass,fs.readFile()
finishes reading the file and its callback which takes 10 ms to complete is added to the poll queue and executed. When the callback finishes, there are no more callbacks in the queue, so the event loop will see that the threshold of the soonest timer has been reached then wrap back to the timers phase to execute the timer's callback. In this example, you will see that the total delay between the timer being scheduled and its callback being executed will be 105ms.
This is what I get from this example:
- when the event loop enters the poll phase and there are timers scheduled, Node processes poll events (if any) or waits for them
- it stops doing this whenever the poll queue is empty and at least on timer's threshold is reached
- when this condition is met, Node goes back to the "timers" phase, i.e. starts a new iteration of the loop
Also, according to the detailed explanation of the "poll" phase, it seems to me that another termination condition for the poll phase is when the queue is empty and setImmediate
work was scheduled. In that case Node goes on with the "check" phase.
So this is my final attempt to make sense of how the event loop actually works:
- During the "timers" phase
setTimeout
andsetInterval
callbacks whose thresholds are reached are executed. - During the "I/O callbacks" phase I/O callbacks for completed (not pending) I/O are executed.
- During the "polling" phase Node waits for pending I/O to be completed. When an I/O operation completes, it goes into the poll event queue. Node processes this events immediately and while doing this, new events may be enqueued. In other words, the queue starts empty and then grows when pending I/O completes (and becomes shorter as poll events are processed). The poll phase stops when one of these happens: A) the poll event queue is empty and one or more timers "are ready", B) the poll event queue is empty and there is
setImmediate
work scheduled, C) a system-dependent hard limit (mesured in number of processed events) is met. In case A the event loop goes back to the "timers" phase, while in case B or C it goes on with the "check" phase. - During the "check" phase any scheduled
setImmediate
work is executed. - During the "close callbacks" phase any close events are processed.
This seems to me a consistent explanation that matches most of what is said in the documentation (which instead seems inconsistent).
Is this explanation correct?
Can we try to make the documentation more clear?