try-catch doesn’t catch an error thrown in timer

JavaScript/TypeScript

try-catch doesn’t catch an error thrown in timer friend functions setTimeout, setInterval and setImmediate.

Sponsored links

Using timer function alone

This code doesn’t work. As you can see, its process dies.

try {
    global.setTimeout(() => {
        console.log("internal process1 working...");
        throw new Error("throw an error1");
    }, 1000);
} catch (e) {
    console.log(e.message);
}

// internal process1 working...

// ...\src\simple-code\timers\app.ts:5
//         throw new Error("throw an error1");
//               ^
// Error: throw an error1
//     at Timeout._onTimeout (...\src\simple-code\timers\app.ts:5:15)
//     at listOnTimeout (internal/timers.js:554:17)
//     at processTimers (internal/timers.js:497:7)
// npm ERR! code ELIFECYCLE
// npm ERR! errno 1
// npm ERR! simple-code@1.0.0 start-timer: `ts-node ./timers/app.ts`
// npm ERR! Exit status 1
// npm ERR!
// npm ERR! Failed at the simple-code@1.0.0 start-timer script.
// npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

// npm ERR! A complete log of this run can be found in:
// npm ERR!

It’s because the callback for the timer friend function isn’t in the same context when it’s triggered. The callback is pushed into a queue and called there.
There are two ways to solve this problem. First one is to define uncaughtException event callback if it is running on Node.js.

process.on("uncaughtException", (e) => {
    console.log(e.message) 
});

// internal process1 working...
// throw an error1

However, this code catches all uncaught Exceptions. It is not handy.
Second way is to define try-catch in the timer function.

global.setTimeout(() => {
    try {
        console.log("internal process2 working...");
        throw new Error("throw an error2");
    } catch (e) {
        console.error(e.message);
    }
}, 1000);
//internal process2 working...
// throw an error2
Sponsored links

Timer function with Promise

You use timer function with Promise if you want to do something after its callback process finishes. Following code doesn’t work.

const promise = new Promise((_, reject) => {
    global.setTimeout(() => {
        console.log("internal process3 working...");
        throw new Error("throw an error3");
    }, 1000);
});
promise.catch((e) => {
    console.log(`error caught in promise.catch.`);
    console.log(e.message);
});
// internal process3 working...

// ...\src\simple-code\timers\app.ts:22
//             throw new Error("throw an error3");
//                   ^
// Error: throw an error3
//     at Timeout._onTimeout (...\src\simple-code\timers\app.ts:22:19)
//     at listOnTimeout (internal/timers.js:554:17)
//     at processTimers (internal/timers.js:497:7)
// npm ERR! code ELIFECYCLE
// npm ERR! errno 1
// npm ERR! simple-code@1.0.0 start-timer: `ts-node ./timers/app.ts`
// npm ERR! Exit status 1
// npm ERR!
// npm ERR! Failed at the simple-code@1.0.0 start-timer script.
// npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

// npm ERR! A complete log of this run can be found in:
// npm ERR!

Call reject function instead when it throws an error.

const promise = new Promise((_, reject) => {
    global.setTimeout(() => {
        try {
            console.log("internal process4 working...");
            throw new Error("throw an error4");
        } catch (e) {
            reject(e);
        }
    }, 1000);
});
promise.catch((e) => {
    console.log(`error caught in promise.catch.`);
    console.log(e.message);
});
// internal process4 working...
// error caught in promise.catch.
// throw an error4

Summary

Enclose your code with try-catch in the timer friend function.
Call reject function if you call it in Promise.

Comments

Copied title and URL