Trust Issues with setTimeout()

Key Concept
setTimeout()does not guarantee the execution of the callback exactly after the given time (e.g., 5000ms). It guarantees that the callback will be executed at least after the given time, but only when the call stack is empty.
Let's Observe the Following Code:
console.log("Start");
setTimeout(function cb() {
console.log("Callback");
}, 5000);
console.log("End");
// Suppose: Millions of lines of code follow...
π§Ύ Output:
Start
End
// After ~10s (not 5s, even though timer is 5s)
Callback
Why Does This Happen?
π Step-by-step Execution:
GEC (Global Execution Context) is created and pushed into the Call Stack.
"Start"is printed.setTimeout(cb, 5000)is encountered:The callback
cb()is sent to the Web APIs environment.A timer of 5 seconds starts running independently.
JavaScript continues immediately without waiting.
"End"is printed.Suppose the remaining code after
"End"takes 10 seconds to executeβblocking the main thread.Meanwhile, the timer expires in 5 seconds, and the callback is pushed to the Callback Queue.
However, since the Call Stack is busy, the Event Loop keeps waiting.
Only after the Call Stack is empty (after 10s), the Event Loop pushes
cb()into the Call Stack.Then the
cb()is executed, printing"Callback".
Concurrency Model of JavaScript
JavaScript uses a Concurrency Model based on:
Call Stack
Web APIs
Callback Queue
Event Loop
π Learn more on MDN: JavaScript Event Loop
π₯ Real-World Example (Blocking the Main Thread)
console.log("Start");
setTimeout(() => {
console.log("Callback after 2s");
}, 2000);
// Simulate heavy CPU task
let startTime = Date.now();
while (Date.now() - startTime < 5000) {
// Blocking main thread for 5s
}
console.log("End");
π§Ύ Output:
Start
End
Callback after 2s
Even though the timer is 2s, it prints after ~5s, because the main thread is blocked.
β οΈ The First Rule of JavaScript:
Never block the main thread.
JavaScript is a single-threaded language. Blocking the Call Stack means blocking everything β rendering, user interaction, animations, and evensetTimeout()callbacks.
What if setTimeout(..., 0)?
console.log("Start");
setTimeout(() => {
console.log("Callback");
}, 0);
console.log("End");
π§Ύ Output:
Start
End
Callback
Why?
Even though the timer is 0ms:
The callback still goes through the Web APIs β Callback Queue β Event Loop.
It waits until the Call Stack is empty.
Useful to defer lower-priority tasks.
π§° Practical Use Case: Deferring Execution
function importantTask() {
console.log("π₯ Important Task");
}
function lessImportantTask() {
console.log("π Less Important Task");
}
// Let important task run first
importantTask();
setTimeout(lessImportantTask, 0);
Even though the timeout is 0, lessImportantTask() waits until the stack is empty.
Summary
| Concept | Explanation |
setTimeout(fn, delay) | Executes fn at least after delay ms |
| JS is single-threaded | Only one Call Stack |
| Callback delay reason | Callback waits in queue until Call Stack is empty |
delay = 0 | Still not executed immediatelyβgoes through Web APIs and queue |
| Event Loop | Continuously checks if Call Stack is empty, then pulls from queue |




