节点的事件循环阶段回调

Geo*_*ris 6 event-loop node.js

我正在阅读有关节点的事件循环阶段的内容,并说

  • 计时器:此阶段执行由 setTimeout() 和 setInterval() 安排的回调。
  • 挂起的回调:执行推迟到下一个循环迭代的 I/O 回调。
  • 空闲,准备:仅内部使用。
  • poll:检索新的I/O事件;执行 I/O 相关回调(几乎所有回调,关闭回调、计时器调度的回调和 setImmediate() 除外);节点会在适当的时候阻塞在这里。
  • check:setImmediate() 回调在这里被调用。
  • 关闭回调:一些关闭回调,例如socket.on('close', ...)。

所以这里我有一个简单的代码来测试上面的一些阶段。当您执行代码时,您会得到以下输出:

  1. 关闭
  2. 即时
  3. 暂停

但根据文档,套接字回调是阶段的最后一个。为什么先执行呢?

let socket = require("net").createServer();

socket.on("data", function (data) {
  console.log(data.toString());
});

socket.on("close", function (data) {
  console.log("close");
});

socket.listen(8080);

const fs = require("fs");

fs.readFile("readme.txt", () => {
  socket.close();
  setTimeout(() => {
    console.log("timeout");
  }, 0);

  setImmediate(() => {
    console.log("immediate");
  });
});
Run Code Online (Sandbox Code Playgroud)

jfr*_*d00 2

首先,请记住,close本地关闭套接字触发的事件不是网络操作。它不是由传入的网络事件触发的。它是由本地套接字实现触发的,决定何时触发关闭事件,以通知监视本地套接字的其他人它现在已关闭。它甚至可以同步触发(如果net实现它的库选择这样做)。因此,您所读到的有关如何在事件循环中对网络事件进行优先级排序或排序的任何内容都不适用于此处。此事件不是由传入网络操作触发的,也不会像传入网络操作那样流经事件循环。

我在调试器中单步调试了您的代码,并socket.close()尝试查看实现的作用。它到达了这条线,这里是调用this._handle.close()。这会进入一些用 C++ 编写的 TCP 包装层,调试器不会让您跟踪它以进一步查看。

然后该函数继续调用emitCloseIfDrained()which 调用并作为要使用的异步机制defaultTriggerAsyncIdScope()传递。process.nextTick()

因此,本地调用似乎会导致套接字库在调用事件之前socket.close()使用,这将赋予它在其他事件之前处理的相当高的优先级,但将在事件循环的未来标记上处理。 process.nextTick()close

但是,我希望您现在可以看到这是如何超级依赖于实现的(取决于在触发事件的任何库中如何触发事件)并且没有记录,因此不应依赖于您的实现。出于求知欲,想要尽可能多地了解这些内容是很好的,但您不应该设计依赖于这种级别的实现细节的代码。诸如close事件在套接字上触发之类的特定功能没有记录,并且不能保证它将来不会改变。如果您的代码需要异步事件的特定顺序,则需要编写代码来管理该顺序,以确保无论这种实现细节级别如何,它都会发生。