我尝试制作一个 Node.js 程序来使用 Promise 连接到一系列 TCP 套接字。 这背后的主要目标只是一个愚蠢的测试:发送消息,然后等待套接字超时或导致连接错误。
然后我遇到了程序的一些不一致的行为,并一直试图去除代码中任何可能的干扰,直到我得到这个片段,这导致了问题:
const net = require('node:net');
function handshake (peer, socket) {
return new Promise ((resolve, reject) => {
socket.setTimeout(3000);
socket.on('timeout', () => (socket.destroy(), reject('timeout')));
socket.on('error', reject);
socket.connect(peer.port, peer.ip, () => socket.write('Hi'));
});
};
(async function () {
// 'peer_list' is an array with 200 objects in this shape:
// { ip:<string>, port:<number> }
const peer_list = require('./peer-list.json');
let promises = [];
for (let peer of peer_list)
promises.push(
handshake(peer, new net.Socket())
);
console.time('Timer');
const result = await Promise.allSettled(promises);
console.timeEnd('Timer');
})();
一些解释:“peer-list.json”中的数据只是所示格式的对象数组。 它们上的 IP 地址是 IPv6 或 IPv4,并且是随机生成的。该文件的副本位于:https://dontpad.com/5aUCGD9H74060377SUW4OzWPP3Du9PXAarnhse
更多解释:我知道
handshake
函数中的承诺永远不会实现,只会被拒绝,但这是事情的一部分,预计套接字会超时(即使地址存在并响应)。
预期输出只是解决所有承诺所需的时间,由
console.timeEnd
打印。但有时它根本不起作用:程序结束,但没有打印任何内容。
在 100 次执行中,只有 40 次有输出。
这意味着
console.timeEnd
有时没有被执行,程序在未完成Promise.allSettled
部分的情况下结束。
我正在一台具有 16GB RAM 和 Intel I5-10400F CPU 的计算机上运行 NodeJS v21.6.2 和 Windows 10 v22H2。任务管理器显示该程序的内存使用率从未超过 10Mb,CPU 使用率从未超过 0.2%。 (我在 VSCode 终端上运行它,但在 CMD 中也发生了同样的情况)。
我知道它显示的内容不多,但这就是连续运行程序 10 次的输出: (它们都在大约相同的时间范围内完成执行:~3-5 秒,但只有 4 个有输出)
我还注意到在
socket.setTimeout(3000)
中使用不同的超时值时发生了变化:当使用较小的值(100 到 400)时,它几乎在 90% 的时间内有效。但我注意到使用更高的值(最高 10000)时没有任何变化。
那么,总而言之,为什么有时会没有执行完就直接退出呢?
在基于 Promise 的代码执行之前,当最后一个套接字完成时,Nodejs 可能会自动退出。我在某些情况下见过这种情况(作为事件循环和解析处理程序调用的解析承诺不完全了解彼此的副作用)。
您可以通过添加
setTimeout(() => {}, 1000*60*60)
来测试这个假设,这只是确保您的 Nodejs 程序不会退出,直到您告诉它(或直到一个小时过去)。然后,为了使程序按预期退出,请在 process.exit()
之后添加 console.timeEnd('Timer');
。