什么是事宜轮回(event loop)?
只管js是单线程的,事宜轮回机制,经由过程在适宜的时刻把操纵交给体系内核,从而许可node实行非壅塞的io操纵
当操纵完成时,内核示知node.js,适宜的回调函数会被到场轮询行列,终究被实行。
Node.js启动的时刻,初始化event loop,处置惩罚供应的剧本,剧本中能够挪用异步API,调理timers,或许挪用process.nextTick(),然后处置惩罚event loop
下图是简化的事宜轮回操纵递次图overview
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
图中每一个box就是一个phase,每一个phase有一个先进先出的回调函数的行列,
event loop进入了一个phase,就会实行phase中一切的操纵,然后实行回调函数,直到行列耗尽了,或许回调函数实行数目抵达最大数,接下来就去下一个phase
由于任何一个操纵都能够调理更多的操纵,而且poll phase中新的事宜由内核列队,所以正在轮询的事宜在被处置惩罚的时刻,poll事宜们能够会列队。
效果:长时候的运转回调函数许可poll phase运转事宜比timer的阈值更长。
phase overview 阶段概略
timers:实行由setTimeout() and setInterval()调理的回调函数
I/O callbacks:实行一切的回调函数,除了 close callbacks(由timers,setImmediate()调理)
idle, prepare:内部运用
poll:猎取新的io事宜,当适宜的时刻,node会壅塞在这里
check: setImmediate()回调函数会在这里挪用
close callbacks: e.g. socket.on(‘close’, …)
每次运转event loop,node搜检是不是有对任何异步io或许timers的守候,没有就封闭
Phases in Detail(各阶段细述)
timers
timers指定阈值(threshold)今后,会实行回调函数,但threshold不是实行回调函数的确实时候(只是最短时候)。
timers回调函数一旦能够实行了就会被实行。但是操纵体系的调理或许其他的回调函数能够推延它的实行。
由poll phase来掌握什么时刻timers被实行
var fs = require('fs');
function someAsyncOperation (callback) {
// Assume this takes 95ms to complete
fs.readFile('/path/to/file', callback);
}
var timeoutScheduled = Date.now();
setTimeout(function () {
var delay = Date.now() - timeoutScheduled;
console.log(delay + "ms have passed since I was scheduled");
}, 100);
// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(function () {
var startCallback = Date.now();
// do something that will take 10ms...
while (Date.now() - startCallback < 10) {
; // do nothing
}
});
一开始timer被调理,内里的回调函数实行log。
然后事宜轮回进入poll phase,此时行列是空的(由于fs.readFile()没有完成),所以就会等着,直到最早的timer的阈值(100)到时候,等了95 ms(还没到,毕竟定的是100),fs.readFile() 这个时刻完成了,所以它的回调函数就回被加poll的行列而且被实行(实行10s),当回调函数完成了,行列又空了,所以,event loop将会看到timer的阈值(100)已到了,
然后回到timers这个phase去实行timers的回调函数,也就是,打印出105秒
为了防备poll phase 独有耗尽 event loop,libuv 也有一个最大值(基于体系),会在凌驾最大值之前住手轮询更多的事宜。
I/O callbacks
为体系操纵(比方tcp毛病范例)实行回调函数
当tcp socket尝试衔接时接收到ECONNREFUSED,类unix体系将会想报导毛病,要会在这个phase列队实行。
poll
poll phase有两个功用
为到了时候的timers实行剧本,然后
处置惩罚poll行列的事宜
当event loop 进入poll phase且没有timers被调理,下面的事变会发作
poll不空,
经由过程回调函数行列迭代的实行
poll栈是空的
假如剧本已被setImmediate()调理,事宜轮回将会停止poll phase,到check phase去实行那些被调理的剧本
等着回调函数被加进行列,然后立马实行它
一旦poll空了,event loop将回搜检timers有无thresholds到了,有的话,wrap back to the timers phase,然后实行timers的回调函数
check
迥殊的 timer
close callbacks
setImmediate and setTimeout()
在poll完成今后实行
在最小事宜今后实行
实行递次:
依赖于挪用的上下文
假如都在main module ,事宜会被历程的机能限定(被其他运用影响)
not within an I/O cycle:不确定的
within an I/O cycle:immediate老是先(更好)
// timeout_vs_immediate.js
setTimeout(function timeout () {
console.log('timeout');
},0);
setImmediate(function immediate () {
console.log('immediate');
});
// timeout_vs_immediate.js
var fs = require('fs')
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout')
}, 0)
setImmediate(() => {
console.log('immediate')
})
})
The Node.js Event Loop, Timers
参考: