我正在阅读有关节点的事件循环阶段的信息,并说
- timers:此阶段执行由setTimeout()和setInterval()安排的回调。
- 待处理的回调:执行推迟到下一个循环迭代的I / O回调。
- 空闲,准备:仅供内部使用。
- poll:检索新的I / O事件;执行与I / O相关的回调(几乎所有情况都与close回调不同, 由计时器和setImmediate()安排;节点将在此处阻塞 适当。
在这里调用- check:setImmediate()回调。
- 关闭回调:一些关闭回调,例如socket.on('close',...)。
所以在这里,我有一个简单的代码来测试上述各个阶段。执行代码时,您将得到以下输出:
但是每个文档的套接字回调都处于最后阶段。为什么要先执行?
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");
});
});
答案 0 :(得分:1)
首先,请记住,您在本地关闭套接字所触发的close
事件不是网络操作。它不是由传入的网络事件触发的。它是由本地套接字实现决定的,该实现决定何时触发close事件,以通知其他监视该本地套接字的人现在已关闭该事件。它甚至可以同步触发(如果实现它的net
库选择这样做)。因此,您所了解的有关事件循环中网络事件的优先级或顺序的所有内容均不适用于此处。此事件不是由传入的网络操作触发的,不会像传入的网络操作那样流经事件循环。
我在调试器中逐步浏览了您的代码,并进入socket.close()
尝试查看实现的工作。到达此行here,其中称为this._handle.close()
。这涉及到一些用C ++编写的TCP包装器层,调试器不允许您深入了解它。
然后,该函数继续调用emitCloseIfDrained()
,后者调用defaultTriggerAsyncIdScope()
并传递给process.nextTick()
作为要使用的异步机制。
因此,看来本地调用socket.close()
会导致套接字库在调用close
事件之前使用process.nextTick()
,这将使其在处理其他事情之前具有较高的优先级,但会在事件循环的将来时间进行处理。
但是,我希望您现在可以看到它是如何依赖于超级实现的(取决于事件是如何在触发它的任何库中触发的),并且没有记录在案,因此不应在实现中依赖它。出于智力好奇的目的,想尽可能多地理解其中的内容是很好的,但是您不应该设计依赖于这种实现细节级别的代码。没有记录诸如close
事件在套接字上触发时之类的特殊功能,并且无法保证将来不会更改。如果您的代码要求对异步事件进行特定的排序,则需要编写代码来管理该序列,以确保无论实现细节级别如何,该序列都会发生。