事件循环就是node.js去作一些非阻塞I/O操做,那么问题来了,非阻塞操做又是什么呢?有一个事实对于js开发者都熟知的是,js是单线程的,也就是说在一段时间内只可以处理一种任务,其余任务要执行须要等待当前任务执行完以后再开始。node
因为大部分的现代内核都是多线程的,它们可以处理不一样的操做执行。当其中的一个操做完成时,内核会告诉node,回调函数将会被加入到执行队列中等待被执行。api
事件循环有好几个阶段,每一个阶段都有先进先出的回调队列被执行。每一个阶段都有它的特别之处,当事件循环进入被给予的阶段时,它将会执行确切的操做,直到上一队列已经执行完接着执行在该阶段的回调,当该阶段上的全部队列或回调都执行完成以后,事件循环会转向下一阶段多线程
一个timer指定了一个阈值,在一个回调被执行以后而不是你想要让它执行的肯定的时间。timer的回调在给定的肯定时间以后将会尽量早的执行,然而,操做系统或者其它回调的执行可能会延迟timer的回调异步
这个阶段执行一些系统操做的回调,例如tcp错误的类型socket
这个poll阶段有两个主要的函数:tcp
当事件循环进入poll阶段,而且没有timers被安排执行,如下之一将会发生:ide
一旦poll队列是空的,事件循环将会检查那些已经到达了设置的阈值的timers。若一个或更多的timers已经准备好,事件循环将会转向执行timers阶段,从而去执行这些timers的回调函数函数
这个阶段在poll阶段已经完成以后容许人立刻执行回调,若是poll阶段变为了闲置状态,且脚本中有setImmediate,事件循环将会转向check阶段而不是等待。 setImmediate其实是一个肯定的timer,它运行在一个事件循环的独立的阶段。它使用了一个libuv api,是在poll阶段完成以后,执行这些安排的回调oop
若是一个socket或者是处理忽然的被关闭了,这个close事件将会在这个阶段触发。另外,它将会经过 process.nextTick触发。ui
他们很相似,可是表现却不一样,这取决于他们什么时候被调用
他们在被调用的不一样环境下执行顺序会有所不一样,然而,若在I/O循环以内,immediate的回调老是先执行
const fs = require('fs');
fs.readFile(__filename, () ={
setImmediate(() ={
console.log('immediate');
});
setTimeout(() ={
console.log('timeout');
}, 0);
});
复制代码
尽管是异步api的一部分,但不是事件循环的部分。这个nextTickQueue将会在当前操做完成以后被处理,无论当前事件循环处在哪一阶段
举个栗子,若是要在函数构造器中触发一个事件,若按照如下写法,是不会被调用执行的,由于该代码并无被处理,构造函数尚未被执行完成。
const EventEmitter = require('events');
const util = require('util');
function MyEmitter() {
EventEmitter.call(this);
this.emit('event'); // 该事件触发无效
}
util.inherits(MyEmitter, EventEmitter);
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
复制代码
若使用了process.nextTick(),就能够在构造器执行完成以后触发该事件,从而触发回调
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(() => {
this.emit('event');
});
}
util.inherits(MyEmitter, EventEmitter);
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
复制代码