is Multiple setTimeout execution single threaded or mutli threaded in Node.js?

Suppose I have 3 setTimeouts like below:

console.log(new Date().toLocaleTimeString());

setTimeout(() => { console.log(new Date().toLocaleTimeString()); }, 3000);

setTimeout(() => { console.log(new Date().toLocaleTimeString()); }, 6000);

setTimeout(() => { console.log(new Date().toLocaleTimeString()); }, 9000);

Since Node is single threaded, execution of each timeout should be one after the other. Hence the output should be as expected as below:

9 am 0s (intial)

9 am 3s(+3s)

9 am 9s(+6s)

9 am 18s(+9s)

But what I found was:

9 am 0s (initial)

9 am 3s(+3s)

9 am 6s(+3s)

9 am 9s(+3s)

So it was clear that all the timeouts have been started at the same time.. difference between each timeout was added to each which in this case was +3s,

Does this make nodejs multi-threaded in this case? I do know that not all parts of node run within the same thread and few do get executed outside the same thread.

Can someone provide me a better sense in this. Or maybe my approach is wrong.

Answer

Since Node is single threaded, execution of each timeout should be one after the other.

Node.js isn’t single-threaded. But it does run a single JavaScript thread by default. You can run others via the worker_threads module. (Each worker gets its own JavaScript environment; they can communicate.)

Hence the output should be as expected as below:

No, it shouldn’t. Your code is starting three timers one right after another. All three calls to setTimeout are run immediately, starting their timer. Then the event loop continues, waiting for something to do, and about three seconds later it sees it’s time for the first timer callback, so it runs that callback. Then the event loop continues. About three seconds after that (six in total), it sees that it’s time for the second callback, and does that. The loop continues…and three seconds later, it does the third callback. This is all on a single thread.

There’s more about the event loop in Node.js here.

Node’s event loop is similar to the browser’s. Here’s your example in a browser, with some additional logging:

const start = Date.now();

function elapsedPrefix() {
    return ("+" + (Date.now() - start)).padStart(4, " ");
}

function log(...msgs) {
    console.log(`${elapsedPrefix()}: ${msgs.join(" ")}`);
}

function now() {
    return new Date().toISOString().substring(11, 12);
}

function startTimeout(num, fn, ms) {
    log(`Start timer ${num} for ${ms}ms`);
    setTimeout(() => {
        log(`Timer ${num} firing`);
        fn();
    }, ms);
}

log(`Starting timers`);

startTimeout(1, () => { log(new Date().toLocaleTimeString()); }, 3000);
startTimeout(2, () => { log(new Date().toLocaleTimeString()); }, 6000);
startTimeout(3, () => { log(new Date().toLocaleTimeString()); }, 9000);

log(`Timers started`);