event-loop定时器阶段执行问题
发布于 5 年前 作者 ddzyan 6391 次浏览 来自 问答

node 版本:v12.9.1

为什么加一个 10 ms 的计时器,会导致100毫秒计时器提前执行呢?我的理解是:

  1. event-loop 在第一次进入 time 阶段监测10秒,执行了 10ms 定时器,
  2. 然后应该直接进入 poll 阶段,执行这个阶段的 fs 文件读取和回调函数内的代码,因为读取文件和回调函数内的代码造成延迟,导致 100ms 计时器实际执行的时间要大于 100ms

可2个定时器的代码,结果输出的和我预想的又不一样,望大神指点。代码如下

代码index.js

const fs = require("fs");

const someAsyncOperation = cb => {
  fs.readFile("./abc.txt", cb);
};

function fib(num) {
  if (num === 0) return 0;
  if (num === 1) return 1;
  return fib(num - 2) + fib(num - 1);
}

const startTime = Date.now();

setTimeout(() => {
  console.log("setTimeout 100 time %s ms", Date.now() - startTime);
}, 100);

setTimeout(() => {
  console.log("setTimeout 10 time %s ms", Date.now() - startTime);
}, 10);

someAsyncOperation(() => {
  console.log("someAsyncOperation time %s ms", Date.now() - startTime);
  const result = fib(40);
  console.log("fib time %s ms", Date.now() - startTime);
});

直接运行,控制台输出如下:

setTimeout 10 time 12 ms
setTimeout 100 time 101 ms
someAsyncOperation time 114 ms
fib time 1354 ms

注释 10 毫秒计时器的代码,控制台输出如下:

someAsyncOperation time 98 ms
fib time 1338 ms
setTimeout 100 time 1341 ms
7 回复

要看someAsyncOperation这个函数执行的时候吧 第二个输出才98ms比100ms少 就先执行了

这个其实和你readFile时间有关,读的快了定时器callback还没放到事件循环里,建议多看看event loop相关的资料

@GaleLQ 我的这段代码采取于官方文档(https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/),按照官方文档的说法,到达每个阶段将执行这个阶段的所有代码,并且将回调函数添加到回调函数队列中,再执行回调函数队列,直到队列用尽或者最大回调函数(函数队列有上限)执行,才进入下一个阶段。

这里我不明白的是,在 time 阶段定时器还未达到阈值,则进入 poll 阶段执行代码和回调函数队列。此时我的理解是需要将 文件读取完毕,回调函数执行完成,再进入 time 阶段,触发 time 的定时器回调函数。

不论文件读取的速度快慢,输出结果应该固定才对,我是否有那部分理解错误了呢…

读取文件和计时功能和事件循环其实没什么关系,是有单独的模块的,你可以看看libuv相关,这两个动作完成之后才会将相应的callback放到事件循环,然后才是你看的事件循环如何执行他们,这是两个过程.

@GaleLQ 我现在的理解是:在同步代码执行完后,会将属于每个阶段的代码分配给对应的模块(每个阶段都有一个处理模块)去处理,模块处理完成后添加回调到对应模块的回调队列中,然后事件循环是在一遍一遍按照顺序检查和执行每个阶段的回调队列。

这样理解是否正确呢?

@ddzyan 可以理解成,同步代码在执行的时候就调用线程执行,不是执行完再调用

@GaleLQ 网上的文章基本没有讲到这一块内容,感谢你的指导,我对 event-loop有了更深入的理解

回到顶部