Mysterious unhandled promise warning from NodeJS

Viewed 3183

When the below code errors (ping() rejects its promise), I get the warning. The HTTP function seems to error out just fine. Something must be happening in ping() itself, I guess, which is somehow avoiding the try-catch.

Could someone enlighten me? (This is after a few attempts at changing things to get it working.)

(async () => {
    try {
            let webTask, pingTask;

            try {
                    webTask = httpsGet(urls[0]);
            } catch (e) {
                    console.log(e);
            }

            try {
                    pingTask = ping('8.8.8.8');
            } catch (e) {
                    console.log(e);
            }

            try {
                    const webResult = await webTask;
                    console.log('HTTP result:', webResult);
            } catch (e) {
                    console.log(e);
            }

            try {
                    const pingResult = await pingTask;
                    console.log('Ping result:', pingResult);
            } catch (e) {
                    console.log(e);
            }
    } catch (e) {
            console.log('Error:', e);
    }
})();

The error is:

"main.js" 137 lines, 2945 characters
(node:58299) UnhandledPromiseRejectionWarning: #<Object>
(node:58299) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:58299) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

This is earlier in the file where I define my ping function:

const netping = require('net-ping');
const pingSession = netping.createSession({
    retries: 0,
    timeout: 10000
});

const ping = ip => {
    return new Promise((resolve, reject) => {
            let result = { ipAddress: ip, start: new Date(Date.now()), end: null, error: null, duration_ms: -1 };

            pingSession.pingHost(ip, (error, target) => {
                    result.end = new Date(Date.now());
                    result.duration_ms = result.end - result.start;
                    if (error) {
                            result.error = error;
console.log('rejecting promise');
                            reject(result);
                    } else {
                            resolve(result);
console.log('resolving promise');
                    }
            });
    });
};

NodeJS 11.13.0

1 Answers

await is the javascript construct which converts the rejection of the promise to an exception. That is, await is the construct which handles the rejection.

When you write:

try {
    pingTask = ping('8.8.8.8');
} catch (e) {
    console.log(e);
}

There's no await there, so there's nothing to convert the rejection to an exception, or indeed to handle the rejection in any way.

If you're going to call ping() without waiting, then you need a more explicit rejection handling.

EDIT

Here's a minimal reproducer:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

const test = (ms, msg) => {
    return new Promise((resolve, reject) => {
        sleep(ms).then(reject(msg)).catch(console.log);
    });
};

(async () => {
    let task1;

    try {
        const ms = 50;
        task1 = test(ms, "hey");
        await sleep(ms * 2); // otherwise you don't get an error
        await task1;
    } catch (e) {
        console.log(e);
    }
})().then(console.log).catch(console.log);

(node:12822) UnhandledPromiseRejectionWarning: hey (node:12822) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:12822) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. hey undefined (node:12822) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

The Promise() constructor starts running test(), after 50 ms the promise is rejected but there is nothing to convert the rejection to an exception. If we remove the 100 ms sleep, then await registers its "convert rejection to exception" logic onto the promise way before the 50 ms sleep is done, and so when the promise gets rejected ~49 ms after await was called, there's a handler to convert it to an exception.

Related