我写过一个小工具,在调用其他几个承诺后返回一个承诺。当我单独测试它时,这个工具很有用,在下面的例子中大约需要10秒。但是,当我尝试将它与http服务器实例一起运行时,如果有的话,需要几分钟才能返回!
我很确定我在这里误解了一些东西,因为我并不是非常精通Node。如果有人能够发现问题,或建议使用promises处理异步方法的替代方法,请告诉我们!
只是为了澄清,它是 traceRoute 函数返回的 Promise.all 。子承诺都按预期解决。
编辑:正如评论中所建议的那样,我还尝试了一个没有调用Promise.all的递归版本;同样的问题。
在没有运行任何 http 服务器实例的情况下调用正常工作独立版本:
const dns = require('dns');
const ping = require('net-ping');
var traceRoute = (host, ttl, interval, duration) => {
var session = ping.createSession({
ttl:ttl,
timeout: 5000
});
var times = new Array(ttl);
for (var i=0; i<ttl; i++){
times[i] = {'ttl': null, 'ipv4': null, 'hostnames': [], 'times': []}
};
var feedCb = (error, target, ttl, sent, rcvd) => {
var ms = rcvd - sent;
if (error) {
if (error instanceof ping.TimeExceededError) {
times[ttl-1].ttl = ttl;
times[ttl-1].ipv4 = error.source;
times[ttl-1].times.push(ms)
} else {
console.log(target + ": " +
error.toString () +
" (ttl=" + ttl + " ms=" + ms +")");
}
} else {
console.log(target + ": " +
target + " (ttl=" + ttl + " ms=" + ms +")");
}
}
var proms = new Array();
var complete = 0
while(complete < duration){
proms.push(
new Promise((res, rej) => {
setTimeout(function(){
session.traceRoute(
host,
{ maxHopTimeouts: 5 },
feedCb,
function(e,t){
console.log('traceroute done: resolving promise')
res(); // resolve inner promise
}
);
}, complete);
})
)
complete += interval;
}
return Promise.all(proms)
.then(() => {
console.log('resolving traceroute');
return times.filter((t)=> t.ttl != null);
});
}
traceRoute('195.146.144.8', 20, 500, 5000)
.then( (times) => console.log(times) )
下面是从服务器实例内部调用的相同逻辑,这是不能正常工作。请参阅内联注释,了解它的确切位置。
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({server: server, path: "/wss"});
const dns = require('dns');
const ping = require('net-ping');
var traceRoute = (host, ttl, interval, duration) => {
var session = ping.createSession({
ttl:ttl,
timeout: 5000
});
var times = new Array(ttl);
for (var i=0; i<ttl; i++){
times[i] = {'ttl': null, 'ipv4': null, 'hostnames': [], 'times': []}
};
var feedCb = (error, target, ttl, sent, rcvd) => {
var ms = rcvd - sent;
if (error) {
if (error instanceof ping.TimeExceededError) {
times[ttl-1].ttl = ttl;
times[ttl-1].ipv4 = error.source;
times[ttl-1].times.push(ms)
} else {
console.log(target + ": " +
error.toString () + " (ttl=" + ttl + " ms=" + ms +")");
}
} else {
console.log(target + ": " + target +
" (ttl=" + ttl + " ms=" + ms +")");
}
}
var proms = new Array();
var complete = 0
while(complete < duration){
proms.push(
new Promise((res, rej) => {
setTimeout(function(){
session.traceRoute(
host,
{ maxHopTimeouts: 5 },
feedCb,
function(e,t){
console.log('traceroute done: resolving promise')
res(); // resolve inner promise
}
);
}, complete);
})
)
complete += interval;
}
console.log('Promise all:', proms);
// #####################
// Hangs on this promise
// i.e. console.log('resolving traceroute') is not called for several minutes.
// #####################
return Promise.all(proms)
.then(() => {
console.log('resolving traceroute')
return times.filter((t)=> t.ttl != null)
});
}
wss.on('connection', function connection(ws, req) {
traceRoute('195.146.144.8', 20, 500, 5000)
.then((data) => ws.send(data));
});
app.use('/tools/static', express.static('./public/static'));
app.use('/tools/templates', express.static('./public/templates'));
app.get('*', function (req, res) {
res.sendFile(__dirname + '/public/templates/index.html');
});
server.listen(8081);
注意:我已尝试在server.listen
之后server.listen
之内从wss.on('connection', ...
内部调用它。这些都没有区别。在服务器正在侦听时,在任何地方调用它会导致它以不确定的方式运行。
答案 0 :(得分:2)
您的代码工作正常!
为了重现这一点,我创建了一个带有工作版本的Dockerfile
。您可以在this git repository中找到它,也可以使用docker pull luxferresum/promise-all-problem
。
您可以使用docker run -ti -p 8081:8081 luxferresum/promise-all-problem
运行泊坞窗图像。这将在localhost:8081
上公开网络服务器。
您也可以使用problematic.js
运行node problematic.js
,然后在网络浏览器中打开localhost:8081
。
将由const ws = new WebSocket('ws://localhost:8081/wss');
打开Web套接字,然后触发代码运行。
实际打开Web套接字非常重要,没有代码将无法运行。
答案 1 :(得分:1)
我建议用其他东西替换跟踪路由,比如DNS查找,看看问题依然存在。此时您无法确定它与raw-socket有关,因为它直接使用libuv句柄,并且不会影响Node.js事件循环的其他部分。