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 |
Subscribe to my newsletter
Read articles from UR Prakash Gupta directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

UR Prakash Gupta
UR Prakash Gupta
My name is ๐๐ ๐๐ซ๐๐ค๐๐ฌ๐ก ๐๐ฎ๐ฉ๐ญ๐ and I talk about ๐ง๐ฒ๐ฐ๐ต-๐๐ง๐จ๐ฐ๐ฅ๐๐๐ ๐, ๐ช๐ฒ๐ฏ๐๐ฒ๐, ๐๐ฒ๐๐ข๐ฝ๐ and ๐๐ถ๐ณ๐ฒ๐๐๐๐น๐ฒ.