Node中的事件循环
Node的底层语言是libuv,使用v8引擎解析js脚本。
libuv负责调用接口API,将不同的任务交给不同的线程处理,再将处理结果交给v8引擎,v8引擎再将处理结果发送给用户。
Node中任务的执行顺序
- timers定时器,执行已经安排的setTimeout和setInterval的回调函数
- pending callback 待定回调:执行延迟到下一个循环迭代的I/O回调
- idle,prepare:仅系统内部使用
- poll:检索新的I/O事件,执行与I/O相关的回调。
- check:执行setImmedate()回调函数
- 关闭的回调函数close callbacks:例如soket.on(‘close’, ()=>{})
Node是跟操作系统打交道的,所以idle/prepare/pending callback暂且不论
相较于浏览器中宏任务为一个队列,可以把Node中看成是6个宏任务队列,每一个队列执行时都会清空当前队列中的回调任务再进行下一个队列
考虑到微任务:process.nextTick和Promise微任务,在每一个队列执行之前,都会去清空nextTick队列再是Promise。
process.nextTick > setImmediate > microtask
setImmediate 和 setTimeout(0)的执行顺序
当我们直接运行时,会看到先执行setTimeout(0)再执行setImmediate
事实是:它们的先后顺序依赖于操作系统的运行速度。setTimeout的程序肯定比setImmediate复杂,他需要创建计时器,因此setTimeout的执行事件是比setImmediate长的。
console.log('start');
let timer1 = setTimeout(() => {
console.log(`settimeout 0`)
process.nextTick(() => {
console.log('settimeout process nextTick');
})
clearTimeout(timer1);
}, 0);
let timer2 = setTimeout(() => {
console.log(`settimeout 300`)
clearTimeout(timer2);
}, 300);
let interval = setInterval(() => {
console.log(`setInterval`)
clearInterval(interval);
}, 0);
process.nextTick(() => {
console.log('process nextTick');
})
const fs = require('fs')
fs.readFile('./test.js', () => {
console.log('poll');
setImmediate(() => {
console.log(`poll setImmediate`)
});
})
setImmediate(() => {
console.log(`setImmediate`);
});
new Promise(resolve => {
console.log('promise start');
resolve();
console.log('promise end');
}).then(() =>{
console.log('promise result');
})
console.log('end');
start
promise start
promise end
end
process nextTick
promise result
settimeout 0
setInterval
settimeout process nextTick
setImmediate
poll
poll setImmediate
settimeout 300
- 打印start
- timer1加入timers队列
- timer2加入timers队列
- interval加入timers队列
- immediate加入check队列
- process.nextTick加入微任务队列
- fs 加入I/O队列
- 执行Promise,打印promise start, promise end 将回调加入异步微任务队列
- 打印script end
- 开始清空微任务队列
- 清空promise.nextTick回调
- 清空promise回调队列
- 开始清空宏任务队列
- 清空timer,setTimeout(0)执行,将process.nextTick加入微任务队列
- setTimeout(300)时间没到,继续放在timers中不执行
- setinterval执行
- timers队列已清空,查找微任务,有,执行process.nextTick
- 开始清空poll队列,此时未到任务执行时机,poll等待并查找其他队列是否有任务
- 由于存在setImmediate回调,poll会等待,先等setImmediate执行完成
- poll队列任务执行时机到了,清空任务,打印poll, 将setImmediate加入check队列
- 清空check队列,打印poll setImmediate
- 一轮循环结束,开启下一轮循环
- 开始清空timers队列
- 此时仅剩setTimeout(300)