美文网首页
从官方说明上解析Nodejs 事件循环

从官方说明上解析Nodejs 事件循环

作者: 某时橙 | 来源:发表于2021-03-11 09:52 被阅读0次

前言

好久没写博客了,因为赶着学业和打码..........鸽了好久抱歉抱歉orz
如果你比较有能力,我完全推荐你直接去看官方文档
https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/
这里是在官方文档的基础上做的一个自己的解析与理解。

正文

事件循环操作顺序:

 ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

本文着重介绍timers(定时器) poll(轮询) check()三个阶段

part 1.定时器阶段(timers)

取自官方:

计时器指定 可以执行所提供回调阈值,而不是用户希望其执行的确切时间。在指定的一段时间间隔后, 计时器回调将被尽可能早地运
行。但是,操作系统调度或其它正在运行的回调可能会延迟它们。

注意轮询 阶段 控制何时定时器执行。

例如,假设您调度了一个在 100 毫秒后超时的定时器,然后您的脚本开始异步读取会耗费 95 毫秒的文件:

const fs = require('fs');

function someAsyncOperation(callback) {
// Assume this takes 95ms to complete
 fs.readFile('/path/to/file', callback);
}

const timeoutScheduled = Date.now();

setTimeout(() => {
 const delay = Date.now() - timeoutScheduled;

 console.log(`${delay}ms have passed since I was scheduled`);
}, 100);

// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(() => {
const startCallback = Date.now();

 // do something that will take 10ms...
 while (Date.now() - startCallback < 10) {
   // do nothing
 }
});

当事件循环进入 轮询 阶段时,它有一个空队列(此时 fs.readFile() 尚未完成),因此它将等待剩下的毫秒数,直到达到最快的一个计时器阈值为止。当它等待 95 毫秒过后时,fs.readFile() 完成读取文件,它的那个需要 10 毫秒才能完成的回调,将被添加到 轮询 队列中并执>行。当回调完成时,队列中不再有回调,因此事件循环机制将查看最快到达阈值的计时器,然后将回到 计时器 阶段,以执行定时器的回调。在本示例中,您将看到调度计时器到它的回调被执行之间的总延迟将为 105 毫秒。
注意:为了防止 轮询 阶段饿死事件循环,libuv(实现 Node.js 事件循环和平台的所有异步行为的 C 函数库),在停止轮询以获得更多事件之前,还有一个硬性最大值(依赖于系统)。

注意到这句话:

当事件循环进入 轮询 阶段时,它有一个空队列(此时 fs.readFile() 尚未完成),因此它将等待剩下的毫秒数,直到达到最快的一个计时器阈值为止。

意味着在轮询阶段有一个阻塞的过程(同步等待意味着阻塞)
这个阻塞并非一成不变,会在轮询队列由 空 - >回调入队(这里是fs.readFile()) 时清空队列,执行回调,此时可能会延迟即将到来的timer函数

大致过程->

[                               ] 0ms
->计算阻塞时间
->等待
[ fs.readFileCallback ] 95ms 这是一个意外的事件(可能不会发生)

->readFileCallback to end 105ms
->检测到可以执行time回调了 虽然已经延迟5ms
->回到timer阶段
->执行time回调

略过了pending callback阶段,因为不影响对事件队列的理解

part 2.轮询阶段(poll)

轮询 阶段有两个重要的功能:

  1. 计算应该阻塞和轮询 I/O 的时间。[1]

  2. 然后,处理 轮询 队列里的事件。
    当事件循环进入 轮询 阶段且 没有被调度的计时器时 ,将发生以下两种情况之一:

如果 轮询 队列 不是空的 ,事件循环将循环访问回调队列并同步执行它们,直到队列已用尽,或者达到了与系统相关的硬性限制。[2]

如果 轮询 队列 是空的 ,还有两件事发生:

如果脚本被 setImmediate() 调度,则事件循环将结束 轮询 阶段,并继续 检查 阶段以执行那些被调度的脚本。

如果脚本 未被 setImmediate()调度,则事件循环将等待回调被添加到队列中,然后立即执行。

一旦 轮询 队列为空,事件循环将检查 已达到时间阈值的计时器。如果一个或多个计时器已准备就绪,则事件循环将绕回计时器阶段以执行这些计时器的回调。

检查阶段
此阶段允许人员在轮询阶段完成后立即执行回调。如果轮询阶段变为空闲状态,并且脚本使用 setImmediate() 后被排列在队列中,则事件循环可能继续到 检查 阶段而不是等待。

setImmediate() 实际上是一个在事件循环的单独阶段运行的特殊计时器。它使用一个 libuv API 来安排回调在 轮询 阶段完成后执行。

通常,在执行代码时,事件循环最终会命中轮询阶段,在那等待传入连接、请求等。但是,如果回调已使用 setImmediate()调度过,并且轮询阶段变为空闲状态,则它将结束此阶段,并继续到检查阶段而不是继续等待轮询事件。

注释[1]猜测和timer阶段的硬性最大值有关,此时要算出本次轮询阶段一共花费多长时间,总不能一直卡着吧!
注释[2]这里的轮询队列说的应该就是事件队列,不过除开官方文章,很多描述事件循环的文章喜欢把它叫做事件队列,这是不是一种长期的误解?

个人简述此阶段:
[1]计算轮询时间T1
[2]处理轮询队列事件
[3]判断轮询队列情况
不空->尽可能在T1内执行队列中的事件
空->等待回调被添加中,然后立即执行
空->(特殊情况)在此期间,如果被setImmediate调度,则直接进入下一阶段。

part 3.检查阶段(check)

此阶段允许人员[1]在轮询阶段完成后立即执行回调。如果轮询阶段变为空闲状态,并且脚本使用 setImmediate() 后被排列在队列中,则事件循环可能继续到检查阶段而不是等待。

setImmediate() 实际上是一个在事件循环的单独阶段运行的特殊计时器。它使用一个 libuv API 来安排回调在 轮询 阶段完成后执行。

通常,在执行代码时,事件循环最终会命中轮询阶段,在那等待传入连接、请求等。但是,如果回调已使用 setImmediate()调度过,并且轮询阶段变为空闲状态,则它将结束此阶段,并继续到检查阶段而不是继续等待轮询事件。[2]

[1]人员?说实话这里不是很懂人员的含义是指程序员吗?但查看英文文档,这里的人员就是people.有些疑惑
[2]这里指的就是上文注解中:空->(特殊情况)在此期间,如果被setImmediate调度,则直接进入下一阶段。
说实话这里的check阶段是专门为 setImmediate准备的,setImmediate为何有这么大能耐?

part 4.关闭的回调函数阶段(close callbacks)

如果套接字或处理函数突然关闭(例如 socket.destroy()),则'close' 事件将在这个阶段发出。否则它将通过 process.nextTick() 发出。

当此阶段结束,node将回到timers进行循环,也就是完成了一轮事件循环、

相关文章

网友评论

      本文标题:从官方说明上解析Nodejs 事件循环

      本文链接:https://www.haomeiwen.com/subject/oejnqltx.html