根据 Node.js 文档, 当事件循环进入轮询阶段并且轮询队列不为空时, 轮询队列中的回调应该在事件循环之前执行 进一步进入检查阶段。
然而,事实上,情况恰恰相反,即如果民意调查和选举都没有 进入事件循环时检查(setImmediate)队列是否为空 poll阶段,来自check(setImmediate)队列的回调总是执行 在轮询队列的回调之前。
这是为什么呢?我在 Node.js 文档中缺少什么?
这里遵循示例代码片段以及来自 Node.js DOC 的引用。
引自 Node.js 文档:
poll阶段有两个主要功能: 1. 计算应该阻塞和轮询 I/O 的时间,然后 2. 处理轮询队列中的事件。 当事件循环进入轮询阶段并且没有调度定时器时, 将会发生以下两种情况之一:
(a) - 如果轮询队列不为空,则事件循环将迭代 通过其回调队列同步执行它们 直到队列耗尽, 或者达到了系统相关的硬限制。
(b) - 如果轮询队列为空,则会发生以下两种情况之一: - 如果脚本已通过 setImmediate() 安排, 事件循环将结束轮询阶段并继续检查阶段 执行那些预定的脚本。 - 如果脚本尚未被 setImmediate() 调度, 事件循环将等待回调被添加到队列中, 然后立即执行它们。 一旦轮询队列为空,事件循环将检查计时器 其时间阈值已达到。如果一个或多个计时器准备就绪, 事件循环将返回到计时器阶段 执行这些计时器的回调。
示例代码:
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.
我知道这个问题很老了,但我遇到了类似的问题,我想我会回答它。
在主循环初始化之前的主初始阶段,Node 实际上会发出一个 open 系统调用来打开文件(在 readFile 中),但不会真正读取它。
如果文件存在,则会在轮询阶段安排一次 io 轮询(以实际处理读取)。轮询阶段没有回调
setImmediate 将具有您安排的回调。
现在循环已初始化,节点到达轮询阶段,没有回调要执行,但有一个事件要轮询,即读取,因此节点执行读取,在等待时,它只是有一个空队列,因此它移动到check 阶段执行 setImmediate 这解释了为什么你首先得到 setImmediateCallback,然后下一个迭代被返回到 poll 阶段,我们等待读取事件完成,回调被添加到 poll 阶段,然后执行。这是输出
设置立即回调 读取文件回调
为什么如果文件不存在,输出就会翻转?因为循环之前的主阶段打开文件,它发现该文件不存在,所以它立即在轮询阶段调度一个带有错误的回调(因为它知道)。
因此,当循环开始时,我们进入轮询阶段,只有一个回调等待我们执行,没有任何事件,因为没有任何内容可读取,我们执行 readFileCallback,然后通过检查阶段 setImmediateCallback 跟进它
这就是为什么输出是这样的
readFileCallback 设置立即回调
顺便说一句,你可以用一个很好的 strace 来验证这一切
strace -f -t -o out.txt node 050-poll.js