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
 */

posted @ 2022-08-07 16:00  IslandZzzz  阅读(331)  评论(0编辑  收藏  举报