事件循环是node处理非阻塞I/O操做的机制。node
当node.js
启动后,会初始化事件轮询;执行脚本,而后启动事件循环。promise
下图是事件循环处理顺序的概览:浏览器
每一个阶段都有一个FIFO队列来执行回调。 当事件循环进入一个阶段,会把该阶段的回调执行完或者达到最大回调数,才会移动到下一个阶段。setTimeout()
和setInterval()
的回调函数。setImmediate()
回调socket.on('close', ...)
。当事件循环进入poll阶段,有两种可能: 1)poll队列不是空的,那么事件循环会将队列里面的回调一个一个执行,直到队列用尽,或者达到最高回调数。 2)poll是空的!,则会去检查有无check阶段回调等待,有的话,移动到check阶段执行。有无到达时间的timers回调,有的话,进入timer阶段执行。若是都没有,则会停留在这个poll阶段,等待回调,而且在有回调进入的时候,当即执行。bash
setImmediate()
对比setTimeout()
处在事件循环不一样阶段,执行时机不一样。 使用 setImmediate()
相对于setTimeout()
的主要优点是,若是setImmediate()
是在 I/O 周期内被调度的,那它将会在其中任何的定时器以前执行,跟这里存在多少个定时器无关,由于poll阶段以后,直接就是check阶段了。异步
process.nextTick()
从技术上讲不是事件循环的一部分,所以并未出如今上图事件循环中。不管在哪一个阶段,process.nextTick()
回调都会在当前回调完成后处理。 这样机制下,如你过递归调用process.nextTick()
,就会'饿死'正常I/O轮询里的回调。socket
const fs = require('fs')
const path = require('path')
console.log('1')
let promise = new Promise(function(resolve){
console.log('new promise')
resolve(1)
})
fs.readFile(path.join(__dirname, './ling.js'), () => {
console.log('readFile')
process.nextTick(function(){
console.log('tick insert')
})
promise.then(function(){
console.log('promise insert')
})
setTimeout(() => {
console.log('timeout')
process.nextTick(function(){
console.log('tick insert in timer')
})
}, 0)
setImmediate(() => {
console.log('immediate')
})
})
fs.readFile(path.join(__dirname, './ling.js'), () => {
console.log('readFile2')
setTimeout(() => {
console.log('timeout2')
}, 0)
setImmediate(() => {
console.log('immediate2')
})
})
promise.then(function(){
console.log('promise then1')
promise.then(function(){
console.log('promise then2')
promise.then(function(){
console.log('promise then3')
})
})
})
process.nextTick(function(){
console.log('tick 1')
process.nextTick(function(){
console.log('tick 3')
})
})
process.nextTick(function(){
console.log('tick 2')
})
console.log('end')
复制代码
执行结果函数
1
new promise // 同步执行,只有.then是异步
end // 第一次同步执行完毕
tick 1 // nextTick队列优先级最高,先一次性清空
tick 2
tick 3
promise then1 // 和nextTick相似,可是优先级没nextTick高
promise then2 // 也是先一次性清空
promise then3
readFile // 进入事件循环poll阶段
tick insert // 执行完poll阶段的一个回调以后,就会去检查nextTick
promise insert // 和 promise的队列有没有回调,有的话执行
readFile2 // 执行完上面的再继续poll阶段下一个回调
immediate // 进入check阶段
immediate2 // check阶段所有执行完
timeout // 进入timers阶段
tick insert in timer // tiemrs执行完一个回调,就会去检查nextTick,和promise
timeout2 // 继续执行timers阶段
复制代码
差别: node的事件循环和浏览器的仍是有差别的,特别是node事件循环存在几个阶段。ui
一致: 在处理promise.then 和process.nextTick 这些微任务,规则是同样的:就是在任什么时候候,只要处理完一个回调,就会去检查,有没有这些微任务,有的话,就一次性执行完毕。再回去继续原来。spa