Promise.all()没有解决时运行服务器 – 否则工作正常

我写了一个小工具,在调用其他许多承诺后返回一个承诺。 这个工具在单独testing时效果很好,下面的例子大概需要10秒。 但是,当我试着将它与一个http服务器实例一起运行的时候,如果根本按照几分钟的顺序返回的话!

我相当肯定,我只是误解了一些东西,因为我不是非常精通Node。 如果任何人都可以发现问题,或build议使用promise来处理asynchronous方法,请告诉我!

只是为了澄清,它是由挂起traceRoute函数返回的Promise.all 。 子承诺全部按预期解决。

编辑:正如评论中所build议的,我也尝试了一个recursion的版本,没有调用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 // ie 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', ...没有任何区别,在服务器正在监听的任何地方调用它,它以非确定性的方式performance。

我不打算接受这个答案,因为这只是一个解决方法; 这个评论太长了

没有任何的承诺,包括Promise.all,都抛出exception。 但是,Node 似乎在停止对Promise.all的呼叫。 我不小心发现,如果我保持超时循环运行,而等待promise.all来解决,那么它实际上会解决,如果预期。

如果有人能够解释到底发生了什么,我真的很喜欢,因为我真的不明白。

 var holdDoor = true var ps = () => { setTimeout(function(){ console.log('status:', proms); if (holdDoor) ps(); }, 500); } ps(); return Promise.all(proms) .then(() => { holdDoor = false console.log('Resolving all!') return times.filter((t)=> t.ttl != null) }); 

你的代码工作得很好!

为了重现这一点,我创build了一个工作版本的Dockerfile 。 你可以在这个git仓库中find它,或者你可以用docker pull luxferresum/promise-all-problem

你可以用docker run -ti -p 8081:8081 luxferresum/promise-all-problem 。 这将暴露localhost:8081上的networking服务器。

您也可以使用node problematic.js运行node problematic.js ,然后在Web浏览器中打开localhost:8081

web套接字将被打开通过const ws = new WebSocket('ws://localhost:8081/wss'); 然后触发代码运行。

它实际上打开networking套接字非常重要,没有代码将无法运行。

我build议用别的东西来replace跟踪路由,比如DNS查找,然后看问题依然存在。 此时,您不能确定它是否与raw-socket有关,因为它直接使用libuv句柄,并且不会影响Node.js事件循环的其他部分。