javascript - Promise.all() not resolving when running server - otherwise works fine - Stack Overflow

I've written a small tool that returns a promise after calling several other promises. This tool w

I've written a small tool that returns a promise after calling several other promises. This tool works great when I test it solo, it takes about 10 seconds in the example below. However, when I try to run it along with a http server instance it, takes in the order of several minutes to return, if at all!

I'm fairly sure I'm just misunderstanding something here, as I'm not extremely proficient in Node. If anyone can spot an issue, or suggest an alternative to using promises for handling asynchronous methods, please let me know!

Just to clarify, it's the Promise.all returned by the traceRoute function which is hanging. The sub-promises are all resolving as expected.

Edit: As suggested in the ments, I have also tried a recursive version with no call to Promise.all; same issue.

This is a working standalone version being called without any http server instance running:

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 plete = 0

    while(plete < 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
                        }
                    );
                }, plete);
            })
        )
        plete += 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) )

Below, is the same logic being called from inside the server instance, this is not working as it should. See the inline ment for where exactly it hangs.

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 plete = 0

    while(plete < 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
                        }
                    );
                }, plete);
            })
        )
        plete += 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);

Note: I have tried calling it before the server.listen, after server.listen, from inside wss.on('connection', .... None of which makes a difference. Calling it anywhere, while the server is listening, causes it to behave in a non-deterministic manner.

I've written a small tool that returns a promise after calling several other promises. This tool works great when I test it solo, it takes about 10 seconds in the example below. However, when I try to run it along with a http server instance it, takes in the order of several minutes to return, if at all!

I'm fairly sure I'm just misunderstanding something here, as I'm not extremely proficient in Node. If anyone can spot an issue, or suggest an alternative to using promises for handling asynchronous methods, please let me know!

Just to clarify, it's the Promise.all returned by the traceRoute function which is hanging. The sub-promises are all resolving as expected.

Edit: As suggested in the ments, I have also tried a recursive version with no call to Promise.all; same issue.

This is a working standalone version being called without any http server instance running:

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 plete = 0

    while(plete < 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
                        }
                    );
                }, plete);
            })
        )
        plete += 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) )

Below, is the same logic being called from inside the server instance, this is not working as it should. See the inline ment for where exactly it hangs.

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 plete = 0

    while(plete < 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
                        }
                    );
                }, plete);
            })
        )
        plete += 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);

Note: I have tried calling it before the server.listen, after server.listen, from inside wss.on('connection', .... None of which makes a difference. Calling it anywhere, while the server is listening, causes it to behave in a non-deterministic manner.

Share Improve this question edited Aug 9, 2017 at 7:33 Richard Dunn asked Aug 4, 2017 at 15:02 Richard DunnRichard Dunn 6,7801 gold badge27 silver badges41 bronze badges 12
  • 1 I take it you get 10 x traceroute done: resolving promise - have you tried, for the sake of debugging, adding a .catch on the Promise.all? – Jaromanda X Commented Aug 5, 2017 at 0:34
  • I haven't actually. I'll give it a look over the next few days. And yes, 10x traceroute to measure jitter... Cheers. – Richard Dunn Commented Aug 5, 2017 at 9:21
  • 1 @JaromandaX Thanks for the tip, but it's not throwing any errors on any of the promises. This is a time issue. It runs okay with the server, but takes about 3 minutes on average, as opposed to 10 seconds. I'm inclined to think it's something to do with how Node HTTP handles sockets, but I'm still investigating this... – Richard Dunn Commented Aug 8, 2017 at 7:53
  • I am not sure why exactly it isn't working, but this looks like it might better be done with a recursive function than creating an array of promises. Something like var trace = //whats in your set timeout, then in the trace function have a set time out to call itself upon pletion if plete < duration. Will write out better what I am thinking later if you still need help. – Joe Lissner Commented Aug 8, 2017 at 18:17
  • @JoeLissner thanks Joe, I'm afraid I've already tried recursion. I moved to Promise.all in the hope that it'd be handled differently, or perhaps trigger a low-level event, but it's the same issue. Can't help but feel it's the 'http' module causing an issue with promises in general. – Richard Dunn Commented Aug 8, 2017 at 20:47
 |  Show 7 more ments

3 Answers 3

Reset to default 6

I'm not going to accept this answer as it's only a workaround; it was just too long to put in the ments...

None of the promises, including the Promise.all, are throwing exceptions. However, Node seems to be parking the call to Promise.all. I accidentally discovered that if I keep a timeout loop running while waiting for the promise.all to resolve, then it will in fact resolve as and when expected.

I'd love if someone could explain exactly what is happening here as I don't really understand.

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)
});

Your code is working perfectly fine!

To reproduce this I've created a Dockerfile with a working version. You can find it in this git repository, or you can pull it with docker pull luxferresum/promise-all-problem.

You can run the docker image with docker run -ti -p 8081:8081 luxferresum/promise-all-problem. This will expose the webserver on localhost:8081.

You can also just run the problematic.js with node problematic.js and then opening localhost:8081 in the web browser.

The web socket will be opened by const ws = new WebSocket('ws://localhost:8081/wss'); which then triggers the code to run.

Its just very important to actually open the web socket, without that the code will not run.

I would suggest replacing the trace route with something else, like a DNS lookup, and see of the issue remains. At this point you cannot be sure it relates to raw-socket, since that uses libuv handles directly and does not effect other parts of the Node.js event loop.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742288085a4415684.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信