Node.js

Nodejs 解读event loop的事件处理机制

2017-09-03  本文已影响1939人  编程go

摘要:

1. nodejs 为什么要存在一个event loop的事件处理机制?
2. event loop的事件处理机制如何运作的?
3. 从event loop机制的角度上区分setImmediate()与setTimeout()
4. 从event loop机制的角度上区分process.nextTick()与setImmediate()

noted:内容来源对nodejs 官方文档学习总结

1. nodejs 为什么要存在一个event loop的事件处理机制?

nodejs 具有事件驱动和非阻塞但线程的特点,使相关应用变得比较轻量和高效。当应用程序需要相关I/O操作时,线程并不会阻塞,而是把I/O操作移交给底层类库(如:libuv)。此时nodejs线程会去处理其他的任务,当底层库处理完相关的I/O操作后,会将主动权再次交还给nodejs线程。因此event loop的作用就是起到调度线程的作用,如当底层类库处理I/O操作后调度nodejs单线程处理后续的工作。也就是说当nodejs 程序启动的时候,它会开启一个event loop以实现异步的api调度、schedule timers 、回调process.nextTick()。

从上也可以看出nodejs 虽说是单线程,但是在底层类库处理异步操作的时候仍然是多线程。

2. event loop的事件处理机制如果运作的?

eventloop.jpg

上述的五个阶段都是按照先进先出的规则执行回调函数。按顺序执行每个阶段的回调函数队列,直至队列为空或是该阶段执行的回调函数达到该阶段所允许一次执行回调函数的最大限制后,才会将操作权移交给下一阶段。

每个阶段的简单概要:

每个阶段的详细内容:

poll phrase.png

Noted:在poll phrase一旦event loop中的poll queue队列为空,poll 就会去timers 查看有没有到期的定时期需要执行。如果有,就会返回timer执行相应的回调函数。

const fs = require('fs');

function someAsyncOperation(callback) {
  // Assume this takes 95ms to complete
  fs.readFile('/Users/spursy/Develop/TFDemo/activateTF.txt', callback);
}

const timeoutScheduled = Date.now();

setTimeout(function() {

  const delay = Date.now() - timeoutScheduled;

  console.log(delay + 'ms have passed since I was scheduled');
}, 100);


// do someAsyncOperation which takes 94 ms to complete
someAsyncOperation(function() {

  const startCallback = Date.now();

  // do something that will take 10ms...
  while (Date.now() - startCallback < 10) {
    // do nothing
  }

});

上面的函数执行的结果是:

104ms have passed since I was scheduled

定时器加入到timer中,定时的时间设置为100秒。poll中执行的I/O操作,由于读取相应目录下的文件要耗费一些时间,poll将会阻塞在这里循环相应的回调函数,大约在94秒时相应的I/O操作执行完毕,对应的回调函数又耗费了10秒钟。这时poll queue为空,此时poll会去timer查看有没有到期的定时器。发现存在一个已经超时近4秒的定时器然后就执行定时器对应的回调函数,这样就是定时器执行了将近104秒钟时间的原因。

3. 从event loop机制的角度上区分setImmediate()与setTimeout()

从Issue 2中poll和check阶段的逻辑,我们可以看出setImmediate和setTimeout、setInterval都是在poll 阶段执行完当前的I/O队列中相应的回调函数后触发的。但是这两个函数却是由不同的路径触发的。

setImmediate函数,是在当前的poll queue对列执行后为空或是执行的数目达到上限后,event loop直接调入check阶段执行setImmediate函数。
setTimeout、setInterval则是在当前的poll queue对列执行后为空或是执行的数目达到上限后,event loop去timers检查是否存在已经到期的定时器,如果存在直接执行相应的回调函数。

如果程序中既有setTimeout和setImmediate,两者的执行顺序是什么?

// timeout_vs_immediate.js
setTimeout(function timeout() {
  console.log('timeout');
}, 0);

setImmediate(function immediate() {
  console.log('immediate');
});

上面的程序执行的结果并不是唯一的,有时immediate在前,有时timeout在qian。主要是由于他们运行的当前上下文环境中存在其他的程序影响了他们执行顺序。

// timeout_vs_immediate.js
const fs = require('fs');

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('timeout');
  }, 0);
  setImmediate(() => {
    console.log('immediate');
  });
});

上面的程序把setImmediate和setTimeout放到了I/O循环中,此时他们的执行顺序永远都是immediate在前,timeout在后。

4. 从event loop机制的角度上区分process.nextTick()与setImmediate()

1. process.nextTick()函数

let bar;

// this has an asynchronous signature, but calls callback synchronously
function someAsyncApiCall(callback) { callback(); }

// the callback is called before `someAsyncApiCall` completes.
someAsyncApiCall(() => {

  // since someAsyncApiCall has completed, bar hasn't been assigned any value
  console.log('bar', bar); // undefined

});

bar = 1;

由于someAsyncApiCall函数在执行时,内部函数是同步的,这是变量bar还没有被赋值。如果改为下面的就会这个异常。

let bar;

function someAsyncApiCall(callback) {
  process.nextTick(callback);
}

someAsyncApiCall(() => {
  console.log('bar', bar); // 1
});

bar = 1;

2. 两者的比较

3. process.nextTick() 函数的应用

function apiCall(arg, callback) {
  if (typeof arg !== 'string')
    return process.nextTick(callback,
                            new TypeError('argument should be string'));
}
const EventEmitter = require('events');
const util = require('util');

function MyEmitter() {
  EventEmitter.call(this);

  // use nextTick to emit the event once a handler is assigned
  process.nextTick(function() {
    this.emit('event');
  }.bind(this));
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on('event', function() {
  console.log('an event occurred!');
});

在MyEmitter构造函数实例化前注册“event”事件,这样就可以保证实例化后的函数可以监听“event”事件。

上一篇 下一篇

猜你喜欢

热点阅读