Event loop的化繁为简(二)

504 阅读2分钟

Node.js的事件循环详细分析

Node.js的事件循环详细分析

为了化简,我将官方的事件循环图进行了遮挡,只留下重点:timers, poll, check这三个对列。

Node.js Event Loop 的理解 Timers,process.nextTick()

Node.js 事件循环

上面这二篇文章对事件循环分析的很清楚了。我就只简要总结。

阶段总览 (Phases Overview)

  • 计时器(timers):执行 setTimeout() 和 setInterval() 的回调,只要时间一到,就把回调放进去。
  • I/O 回调: 执行几乎全部发生异常的 close 回调, 由定时器和setImmediate()计划的回调;
  • 空闲,预备(idle,prepare):只内部使用;
  • 轮询(poll): 获取新的 I/O 事件;nodejs这时会适当进行阻塞;
  • 检查(check): 执行 setImmediate() 的回调;
  • close callbacks: 例如 socket.on('close', ... );
var fs = require('fs');

function someAsyncOperation (callback) {
  // 花费2毫秒
  fs.readFile(__dirname + '/' + __filename, callback);
}

var timeoutScheduled = Date.now();
var fileReadTime = 0;

setTimeout(function () {
  var delay = Date.now() - timeoutScheduled;
  console.log('setTimeout: ' + (delay) + "ms have passed since I was scheduled");
  console.log('fileReaderTime',fileReadtime - timeoutScheduled);
}, 10);

someAsyncOperation(function () {
  fileReadtime = Date.now();
  while(Date.now() - fileReadtime < 20) {

  }
});
// 运行结果
setTimeout: 22ms have passed since I was scheduled
fileReaderTime 2

运行过程说明

  1. 首先将代码全部放到主线列队列中全部运行。下面代码运行完毕。
var fs = require('fs');

function someAsyncOperation (callback) {
  // 花费2毫秒
  fs.readFile(__dirname + '/' + __filename, callback);
}

var timeoutScheduled = Date.now();
var fileReadTime = 0;
  1. 当运行到setTimeout时,
setTimeout(function () {
  var delay = Date.now() - timeoutScheduled;
  console.log('setTimeout: ' + (delay) + "ms have passed since I was scheduled");
  console.log('fileReaderTime',fileReadtime - timeoutScheduled);
}, 10);

node.js会启动一个定时线程定时,程序继续。

  1. 在运行到
someAsyncOperation(function () {
  fileReadtime = Date.now();
  while(Date.now() - fileReadtime < 20) {

  }
});
  1. 发现有
fs.readFile(__dirname + '/' + __filename, callback);

这里又会启动一个文件读取线程去读文件。

  1. 这时程序发现poll里已经没有任务,就会阻塞在这里。
  2. 然后2ms以后,fs.read完成,就将回调打入poll里。
  3. 程序将一次性清空poll队列内容,放入主循环队列,一次执行。再次期间之前的setTimeout到时间,将回调打入到timers队列。
  4. 主循环队列运行完毕,检查check队列,就清空timers队列,再放入主循环队列,运行。
  5. 因为有了之前的运行时间消耗,所以等到运行setTimeout回调的时候,时间就到了22ms。
  6. 程序继续回到poll,监听其它动静。

process.nextTick()

process.nextTick()不在event loop的任何阶段执行,而是在各个阶段切换的中间执行,即从一个阶段切换到下个阶段前执行。

阶段切换就是指清空队列以后。

与浏览器事件的区别

最大区别就是,浏览器的事件循环队列是一个一个的弹出,而Node.js是一次性清空任务队列。

Javascript Event-loop的化繁为简(一)