Promise.all()在运行服务器时无法解析 - 否则工作正常

时间:2017-08-04 15:02:30

标签: javascript node.js promise

我写过一个小工具,在调用其他几个承诺后返回一个承诺。当我单独测试它时,这个工具很有用,在下面的例子中大约需要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', ...内部调用它。这些都没有区别。在服务器正在侦听时,在任何地方调用它会导致它以不确定的方式运行。

2 个答案:

答案 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事件循环的其他部分。