事宜轮回中的各阶段
Node.js 的事宜轮回流程大抵以下:
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
每一个阶段都有本身的使命行列,当本阶段的使命行列都实行终了,或许到达了实行的最大使命数,就会进入到下一个阶段。
timers 阶段
这个阶段会实行被 setTimeout
和 setInterval
设置的定时使命。
固然,这个定时并非正确的,而是在超过了定时时候后,一旦获得实行时机,就马上实行。
pending callbacks 阶段
这个阶段会实行一些和底层体系有关的操纵,比方TCP衔接返回的毛病等。这些毛病发作时,会被Node 推晚到下一个轮回中实行。
轮询阶段
这个阶段是用来实行和 IO 操纵有关的回调的,Node会向操纵体系讯问是不是有新的 IO 事宜已触发,然后会实行相应的事宜回调。险些一切除了 定时器事宜、 setImmediate()
和 close callbacks
以外操纵都邑在这个阶段实行。
check 阶段
这个阶段会实行 setImmediate()
设置的使命。
close callbacks 阶段
假如一个 socket
或 handle(句柄)
倏忽被封闭了,比方经由历程 socket.destroy()
封闭了,close
事宜将会在这个阶段发出。
事宜轮回的详细实行
事宜轮回初始化以后,会依据上图所示的流程举行:
- 起首会顺次实行 定时器中的使命、
pending callback
回调; - 然后进入到
idle
、prepare
阶段,这里会实行 Node 内部的一些逻辑; - 然后进入到
poll
轮询阶段。在这个阶段会实行一切的 IO 回调,如 读取文件,收集操纵等。poll
阶段有一个poll queue
使命行列。这个阶段的实行历程相对较长,详细以下:
- 进入到本阶段,会先搜检
timeout
定时行列是不是有可实行的使命,假如有,会跳转到定时器阶段
实行。 - 假如没有
定时器使命
,就会搜检poll queue
使命行列,假如不为空,会遍历实行一切使命直到都实行终了或许到达能实行的最大的使命数目。 -
poll queue
使命行列实行完成后,会搜检setImmediate
使命行列是不是有使命,假如有的话,事宜轮回会转移到下一个check
阶段。 - 假如没有
setImmediate
使命,那末,Node 将会在此守候,守候新的 IO 回调的到来,并马上实行他们。
注重 :这个守候不会一向守候下去,而是到达一个限制前提以后,继承转到下一个阶段去实行。
setTimeout()
和 setImmediate()
一个小隐秘
实在也不算隐秘,只是我是在方才查阅材料才晓得的。
那就是:在 Node 中,setTimeout(callback, 0)
会被转换为 setTimeout(callback, 1)
。
详情请参考 这里 。
setTimeout()
和 setImmediate()
的实行递次
下面这两个定时使命实行的递次在差别情况下,表现不一致。
setTimeout(function() {
console.log('timeout');
}, 0);
setImmediate(function() {
console.log('immediate');
});
一般代码中设置定时器
假如在一般的代码实行阶段(比方在最外层代码块中),设置这两个定时使命,他们的实行递次是不牢固的。
- 起首,我们设置的
setTimeout(callback, 0)
已被转换成为setTimeout(callback, 1)
,所以进入定时器
阶段时,会依据当前时候推断定时是不是超过了1ms
。 - 事宜轮回在进入定时器阶段之前会由体系挪用方法来更新当前时候,由于体系中同时运转着其他的顺序,体系须要守候其他顺序的历程运转完毕才猎取正确时候,所以更新获得的时候能够会有肯定的耽误。
- 更新时候时,若没有耽误,定时不到
1ms
,immediate
使命会先实行;假如存在耽误,而且这个时候到达了1ms
的界线,timeout
使命就会起首实行。
在IO回调中设置定时器
假如我们在 IO 回调中设置了这两个定时器,那末 setImmediate
使命会起首实行,缘由以下:
- 进入
poll phase
轮询阶段之前会先搜检是不是有timer
定时使命。 - 假如没有
timer
定时使命,才会实行背面的 IO 回调。 - 我们在 IO 回调中设置
setTimeout
定时使命,这时候已过了timer
搜检阶段,所以timer
定时使命会被推晚到下一个轮回中实行。
process.nextTick()
不管在事宜轮回的哪一个阶段,只需运用 process.nextTick()
添加了回调使命,Node 都邑在进入下一阶段之前把 nextTickQueue
行列中的使命实行完。
setTimeout(function() {
setImmediate(() => {
console.log('immediate');
});
process.nextTick(() => {
console.log('nextTick');
});
}, 0);
// nextTick
// immediate
上述代码中,老是先实行 nextTick
使命,就是由于在轮回在进入下一个阶段之前会先实行 nextTickQueue
中的使命。下面代码的实行效果也相符预期。
setImmediate(() => {
setTimeout(() => {
console.log('timeout');
}, 0);
process.nextTick(() => {
console.log('nextTick');
});
});
// nextTick
// timeout