Nodejs event loop

《Nodejs event loop》

node 执行完主线程中的同步任务时, 它会检查是否有等待中的异步操作, 如果有,则会开始事件循环, 否则它会退出.

事件循环的 6 个阶段

  • timer

    • 执行由 setTimeoutsetInterval 设置的回调
  • pending callbacks

    • 执行被延迟的 I/O 回调
  • idle, prepare

    • 内部操作
  • poll

    • 获取新的 I/O 事件(eg. connectionrequest), 执行 I/O 相关回调(除了 close callbackstimercheck)
  • check

    • 执行由 setImmediate 设置的回调
  • close callbacks

    • 执行一些关闭操作回调(eg. socket.destroy)

这些异步操作, 可在入口脚本中被添加至指定的模块(eg. 定时器模块), eg:

//entry.js

console.log(1);
//异步操作, 就绪时, 其回调被添加进 timer 队列中
setTimeout(()=>console.log('timeout callback'),0);
console.log(2);
//异步操作, 其回调被添加进 check 队列中
setImmediate(()=>console.log('immediate callback'));
console.log(3);

当某个异步操作被就绪时(eg. 定时器到达指定时间), 该异步操作的回调就会被添加到相应阶段的队列中(eg. 定时器的回调被添加到 timer 阶段的队列中),等待被执行.

执行顺序

node 按照上图的顺序执行每个阶段的队列, 当队列为空时, 它会转入下一阶段. 当执行 poll 阶段时, 它按照以下逻辑执行:

  • poll 的队列不为空时, 它会执行其队列中的回调, 直到其为空或者达到系统的限制
  • poll 的队列为空且没有定时器就绪时

    • 如果有 setImmediate 就绪, 则会结束 poll 阶段, 转入 check 阶段
    • 计算距离下一个定时器就绪的时间, 在此之前,停在 poll 阶段, 等待回调被加入其队列

poll 执行完其队列后, 会检查是否有定时器就绪, 如果有, 则会结束 poll 阶段, 继续本轮循环的执行, 然后进入下一轮循环中执行就绪的定时器.

setTimeout 与 setImmediate

当在非 I/O 周期中执行 setTimeoutsetImmediate 时, 其顺序依赖于当前系统的环境. 如果系统运行到事件循环开始, 超过了定时器设定的时间( setTimeout 的第二个参数默认值为 0, 但是 node 达不到 0ms 就执行定时器的要求, 实际值从 1ms 开始), 则 setTimeout 先于 setImmediate 被执行, 反之 setImmediate 先于 setTimeout.

setTimeout(()=>console.log('timeout'),0);
setImmediate(()=>console.log('immediate'));
//output: 1 2 or 2 1

当在 I/O 周期中执行时, setImmediate 先于 setTimeout 执行. 因为执行 I/O 操作时为 poll 阶段, poll 阶段结束后, 就转入了 check 阶段.

const fs = require('fs');
fs.readFile(__filename,()=>{
    setTimeout(()=>console.log('timeout'),0);
    setImmediate(()=>console.log('immediate'));
})
//output: immediate timeout

process.nextTick

process.nextTick 在所有同步操作执行完毕之后、事件循环开始之前执行、每个阶段执行之前执行

setTimeout(()=>console.log('timeout'),0);
process.nextTick(()=>console.log('nextTick'));
console.log('entry');
//output: entry nextTick timeout
setTimeout(()=>{
    console.log('timeout1');
    process.nextTick(()=>console.log('nextTick'));
},0);
setTimeout(()=>console.log('timeout2'));
//output: timeout1 timeout2 nextTick
setTimeout(()=>{
    console.log('wrap-timeout');
    setTimeout(()=>console.log('inner-timeout'),0);
    process.nextTick(()=>{
        console.log('wrap-nextTick');
        process.nextTick(()=>console.log('inner-nextTick'));
    });
},0);
//output: wrap-timeout wrap-nextTick inner-nextTick inner-timeout
    原文作者:movechip
    原文地址: https://segmentfault.com/a/1190000020044737
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞