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

Viewed 1383

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 comments, 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 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) )

Below, is the same logic being called from inside the server instance, this is not working as it should. See the inline comment 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 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);

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.

3 Answers
Related