深入浅析Node.js 事件循环、定时器和process.nextTi(2)

const fs = require('fs'); function someAsyncOperation(callback) { // Assume this takes 95ms to complete fs.readFile('/path/to/file', callback); } const timeoutScheduled = Date.now(); setTimeout(() => { const delay = Date.now() - timeoutScheduled; console.log(`${delay}ms have passed since I was scheduled`); }, 100); // do someAsyncOperation which takes 95 ms to complete someAsyncOperation(() => { const startCallback = Date.now(); // do something that will take 10ms... while (Date.now() - startCallback < 10) { // do nothing } });

当事件循环进入轮询阶段时,它有一个空队列( fs.readFile() 尚未完成),因此它将等待剩余的ms数,直到达到最快的计时器阈值。 当它等待95毫秒传递时, fs.readFile() 完成读取文件,并且其完成需要10毫秒的回调被添加到轮询队列并执行。 当回调结束时,队列中不再有回调,因此事件循环将看到已达到最快定时器的阈值,然后回绕到定时器阶段以执行定时器的回调。 在此示例中,您将看到正在调度的计时器与正在执行的回调之间的总延迟将为105毫秒。

注意:为了防止轮询阶段使事件循环挨饿,libuv(实现Node.js事件循环的C库和平台的所有异步行为)在停止轮询之前也为事件提供了固定的最大值(取决于系统)。

等待回调(pending callbacks)

此阶段执行某些系统操作(例如TCP错误类型)的回调。 例如,如果TCP套接字在尝试连接时收到 ECONNREFUSED ,则某些*nix系统希望等待报告错误。 这将排队等待在等待回调阶段执行。

轮询(poll)

轮询阶段有两个主要功能:

1.计算它阻塞和轮询I / O的时间,然后
2.处理轮询队列中的事件。

当事件循环进入轮询阶段并且没有定时器调度时,将发生以下两种情况之一:

如果轮询队列不为空,则事件循环将遍历回调队列并且同步执行,直到队列已执行完或者达到系统相关的固定限制。

如果轮询队列为空,则会发生以下两种情况之一:

setImmediate()
setImmediate()

检查(check)

此阶段允许在轮询阶段完成后立即执行回调。 如果轮询阶段变为空闲并且脚本已使用 setImmediate() 排队,则事件循环可以继续到检查阶段而不是等待。

setImmediate() 实际上是一个特殊的计时器,它在事件循环的一个单独阶段运行。 它使用libuv API来调度在轮询阶段完成后执行的回调。

通常,在执行代码时,事件循环最终会到达轮询阶段,它将等待传入连接,请求等。但是,如果已使用 setImmediate() 调度回调并且轮询阶段变为空闲,则 将结束并继续检查阶段,而不是等待轮询事件。

关闭回调(close callbacks)

如果套接字或句柄突然关闭( 例如socket.destroy() ),则在此阶段将发出 'close' 事件。 否则它将通过 process.nextTick() 发出。

setImmediate() vs setTimeout()

setImmediate 和 setTimeout() 类似,但根据它们的调用时间以不同的方式运行。

setImmediate()
setTimeout()

执行定时器的顺序将根据调用它们的上下文而有所不同。 如果从主模块中调用两者,则时间将受到进程性能的限制(可能受到计算机上运行的其他应用程序的影响)。

例如,如果我们运行不在I / O周期内的以下脚本(即主模块),则执行两个定时器的顺序是不确定的,因为它受进程性能的约束:

// timeout_vs_immediate.js setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); }); $ node timeout_vs_immediate.js timeout immediate $ node timeout_vs_immediate.js immediate timeout

但是,如果在I / O周期内移动两个调用,则始终首先执行立即回调:

// timeout_vs_immediate.js const fs = require('fs'); fs.readFile(__filename, () => { setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); }); }); $ node timeout_vs_immediate.js immediate timeout $ node timeout_vs_immediate.js immediate timeout

使用 setImmediate() 而不是 setTimeout() 的主要优点是 setImmediate() 将始终在任何定时器之前执行(如果在I / O周期内调度),与存在多少定时器无关。

process.nextTick()

理解 process.nextTick()

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/0d73fac86c76b4edf72e25021c9fcde6.html