Nodejs 事件循环 setImmediate setTimeout nextTick
事件循环允许Nodejs执行非阻塞I/O操作,尽管JavaScript是单线程的,但是可以将尽可能多的操作让系统内核去执行。
现代系统大多数内核都是多线程的,因此它们可以处理在后台执行的多个操作。
当一个操作完成时,系统就可以告诉nodejs, 以便可以将对应的回调添加到任务队列最终执行
Nodejs 事件循环流程
事件: EventEmitter
非阻塞I/O: 网络请求,文件读写
脚本: js脚本执行
timers -> pending callback -> poll
-> check -> close callbacks -> timers
或
-> timers
timers: 执行setTimeout和setInterval执行的回调函数
pending callback:某些系统操作的回调, 执行除了定时器以外的几乎所有回调函数
poll:轮询等待新的链接和请求等事件,执行 I/O 回调等
check: setImmediate的回调会在此刻执行
closeCallbacks: 关闭回调执行,如 socket.on('close', ...)
poll轮询机制
如果代码进入poll阶段
1. poll queue不为空,那么同步执行掉pull queue
2. poll 为空
如果没有timer和setImmediate,那么阻塞在poll阶段,等待下一个异步io回调进入
如果有setTimeout,进入timer阶段
如果有setImmediate,进入check阶段
如果都有,取决于eventLoop的启动速度,可能是setTimeout(,0)也可能是setImmediate先执行
setTimeout(cb,0)和setImmediate的区别
setImmediate约等于setTimeout(fn,0),都是宏任务
任务队列的启动也是有时间得,setTimeout(fn,0)不一定是0毫秒就执行
如果任务队列到达poll时,时间是0.8ms, 那么setTimeout可能在1ms执行, 那么就会是setImmediate先执行,setTimeout后执行
如果任务队列到达poll时,时间是1.5ms, 那么setTimeout先执行,setImmediate后执行
因此,setImmediate和setTimeout(fn,0)的执行顺序,取决于eventloop的启动速度
如果是在io回调如fs.readFile中,那么事件循环肯定处于poll阶段,之后进入check阶段执行setImmediate, 最后才执行timers setTimeout
// 执行顺序不确定
setImmediate(()=>{
console.log('setImmediate');
})
setTimeout(() => {
console.log('timeout')
}, 0);
// 执行顺序是确定的, 先setImmediate在setTimeout
require('fs').readFile('./record/01介绍.md',{encoding:'utf-8'},(err,data)=>{
setTimeout(() => {
console.log('timeout 2')
}, 0);
setImmediate(() => {
console.log('setImmediate 2');
})
})
process.nextTick
属于微任务
不在eventloop的任何一个阶段执行,而是在各个阶段切换的中间执行
即一个阶段切换到下一个阶段前执行, 如poll和check切换的中间会执行nextTick
nextTick应用场景
多个事件中交叉执行cpu运算密集型任务,比如服务器http请求的时候,还能抽空进行一些计算任务
如果不用nextTick而是同步计算,那么会卡在这里
另外一个场景就是保证订阅在发布之前,即nextTick包装一下emit
nextTick执行顺序
require('fs').readFile('./record/01介绍.md',{encoding:'utf-8'},(err,data)=>{
setTimeout(() => {
console.log('timeout')
}, 0);
process.nextTick(()=>{
console.log('nectTick 1');
})
process.nextTick(()=>{
console.log('nectTick 2');
})
Promise.resolve().then(v => {
console.log('Promise')
})
setImmediate(() => {
console.log('setImmediate');
process.nextTick(()=>{
console.log('nectTick 3');
})
})
})
/**
nectTick 1
nectTick 2
Promise
setImmediate
nectTick 3
timeout
*/