4

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.
Igor C.
  • 41
  • 2
  • ... I've got different behavior when passing an other path. There is [a check in the source](https://github.com/nodejs/node/blob/d07c12f4441ff389f67868c6535c76bec7196cd6/lib/fs.js#L380) to see if the path is a field decriptor, in which case they wait the `nextTick()` before actually *starting* the request, which would be a great explanation, except that their check is actually just [isUint32](https://github.com/nodejs/node/blob/d07c12f4441ff389f67868c6535c76bec7196cd6/lib/internal/validators.js#L39) and that `__filename` is not an Uint32... – Kaiido Jun 07 '21 at 03:04
  • This may answer your concern: _"Generally, as the code is executed, the event loop will eventually hit the poll phase where it will wait for an incoming connection, request, etc. However, if a callback has been scheduled with setImmediate() and the poll phase becomes idle, it will end and continue to the check phase rather than waiting for poll events."_ – Hoang Dao Jun 07 '21 at 08:09
  • @HoangDao the question here is *why is the poll phase idle while the fs callback should already have been pushed there*. – Kaiido Jun 07 '21 at 09:25
  • @Kaiido, I was unable to find a path that would have resulted in different behavior. Just curious, which one made the difference for you? – Igor C. Jun 07 '21 at 18:32
  • Random strings like "foo", or "", but I may have tried it only on an old node (10). I'll have to test again on an up to date one if you say it doesn't repro for you. – Kaiido Jun 07 '21 at 22:34
  • 2
    @Kaiido, I tried non-existing paths, like 'foo', and indeed that reversed the outcome, which means that there is a radical difference in the outcome depending on whether the path is real or fake. This proves that there is obviously a shortfall of my understanding of how the fs.readFile() works, so I'll have to fix it before digging any deeper into the event-loop. Thank you for the suggestion to try a fake path, it made the difference. – Igor C. Jun 08 '21 at 00:09

0 Answers0