深入理解nodejs Event loop

什么是事件循环
  • Event loop 允许node.js执行非阻塞I/O操作(尽管JS是单线程的),也就是在相应情况下,尽可能的将任务交给系统内核。

  • 多数内核是多线程的,可以同时处理多个任务。当其中一个任务完成时,相应的callback被插入到轮询队列中,最终被执行。

事件循环
  • 事件循环包含几个阶段,相应的阶段做相应的事
  • 初始化: Node.js启动后,会进行一些初始化
    1. 初始化Event loop
    2. 处理目标脚本
    3. 然后进入事件循环

这里写图片描述

  • 每个阶段,都有其FIFO队列,用来执行回调函数。
    1. 每个阶段都是特殊的。
    2. 当进行到该阶段时,会执行该阶段特有的操作,然后执行该阶段队列中的回调。
    3. 当队列空,或者达到执行次数限制,事件循环进行下阶段。
    4. 循环往复。
阶段总览
  1. timers:执行setTimeout()和setInterval安排的回调
  2. I/O callbacks: 执行几乎所有异常的close回调,由timer和setImmediate执行的回调。
  3. idle,prepare: 只用于内部
  4. poll : 获取新的I/O事件,node在该阶段会适当的阻塞
  5. check : setImmediate的回调被调用
  6. close callbacks: e.g socket.on(‘close’,…);
  7. 在每次运行事件循环之间,node.j检查是否有正在等待的异步i/o调用、timers等。如果没有,就清除并结束(退出程序),例如:执行一个程序,仅有一句话(var a= ‘hello’;),处理完目标代码后,不会进入evetloop,而是直接结束程序。

每个阶段

Timers:定时器可以设置回调函数在指定时间后运行。这个回调函数会在能够被调度后,尽可能快的运行。操作系统的调度,或其他回调数的运行,可能会延迟该回调函数的运行。**通常,poll阶段控制定时器的运行。
  • 当事件循环进入poll阶段,根据该阶段队列内容执行:
    1. 执行队列中的回调函数
    2. 当队列中没有回调函数时,且定时器设置时间已过
    3. 回到timers 阶段,执行相应的回调函数
    4. **为了防止poll阶段过长,libuv根据为不同系统设置了poll阶段的最长事件。
I/O callbacks:该阶段执行一些如TCP错误之类的系统操作的会ID回调。
Poll(轮询):
  • 两个主要功能

    1. 执行定时器设置并到期的回调函数,然后
    2. 处理poll队列中的事件
  • 工作机制:当没有timers被调度,分两种情况

    1. 如果poll队列不为空,会挨个执行队列里的callback,直到队列为空,或达到系统限制
    2. 如果poll队列为空,分两种情况:

如果执行了setImmediate(),eventloop会结束poll阶段,进入到check阶段执行
如果没有执行setImmediate(),eventloop会等待callback进入队列

  • 一旦poll队列为空,evetloop会检查timers,如果计时已到,event loop 会回到 timers 阶段,执行相应的回调函数.
check阶段
  • poll阶段变为空闲、等待状态时,一旦调用setImmediate(),eventloop会进入check 阶段,而不是在poll阶段等待。
close callbacks阶段
  • 例如:socket或句柄关闭,close事件会触发这个阶段。或者通过process.nextTick()触发

总结

  • Node.js事件循环有实际上7、8个阶段,但我们关注-node实际使用的,就是前面说的几个。每个阶段都有自己的队列。本阶段执行完成后,执行下一个阶段。循环顺序不是完全固定的,很多阶段都是由外部事件触发的。

重要的三阶段:

  • timers,定时器阶段:
    执行定时任务(setTimeOut(), setInterval())

  • poll 轮询阶段:

    1. 处理到期的定时器任务,然后(因为最开始阶段队列为空,一旦队列为空,就会检查是否有到期的定时器任务)
    2. 处理队列任务,直到队列空,或达到上限
    3. 如果队列为空:如果setImmediate,终止轮询阶段,进入检查阶段执行。如果没setImmediate,查看有没有定时器任务到期,有的话就到timers阶段,执行回调函数.
  • check 检查阶段

    1. 轮询阶段空闲,且有setImmediate的时候,进入检查阶段

不重要二阶段

  • I/O callbacks阶段:处理I/O异常错误
  • close callbacks阶段: 处理各种close事件回调

代码

  • 关于poll阶段和timers阶段调用先后
const fs = require('fs');

setTimeout(function(){
    console.log('ff');
},0);

function func(cb) {
    fs.readFile('xxx.js',cb);
}

func( () => {
    console.log('a');
})
Node.js系列
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林多

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值