Skip to main content

Command Palette

Search for a command to run...

Trust Issues with setTimeout()

Updated
β€’3 min read
Trust Issues with setTimeout()
P

My name is 𝐏𝐫𝐚𝐀𝐚𝐬𝐑 and I talk about 𝗧𝗲𝗰𝗡-𝐊𝐧𝐨𝐰π₯𝐞𝐝𝐠𝐞, π—ͺπ—²π—―π——π—²π˜ƒ, π——π—²π˜ƒπ—’π—½π˜€ and π—Ÿπ—Άπ—³π—²π˜€π˜π˜†π—Ήπ—².

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:

  1. GEC (Global Execution Context) is created and pushed into the Call Stack.

  2. "Start" is printed.

  3. 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.

  4. "End" is printed.

  5. Suppose the remaining code after "End" takes 10 seconds to executeβ€”blocking the main thread.

  6. Meanwhile, the timer expires in 5 seconds, and the callback is pushed to the Callback Queue.

  7. However, since the Call Stack is busy, the Event Loop keeps waiting.

  8. Only after the Call Stack is empty (after 10s), the Event Loop pushes cb() into the Call Stack.

  9. 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 even setTimeout() 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

ConceptExplanation
setTimeout(fn, delay)Executes fn at least after delay ms
JS is single-threadedOnly one Call Stack
Callback delay reasonCallback waits in queue until Call Stack is empty
delay = 0Still not executed immediatelyβ€”goes through Web APIs and queue
Event LoopContinuously checks if Call Stack is empty, then pulls from queue

More from this blog

Learn with Prakash

36 posts

My name is 𝐏𝐫𝐚𝐀𝐚𝐬𝐑 and I talk about 𝗧𝗲𝗰𝗡-𝐊𝐧𝐨𝐰π₯𝐞𝐝𝐠𝐞, π—ͺπ—²π—―π——π—²π˜ƒ, π——π—²π˜ƒπ—’π—½π˜€ and π—Ÿπ—Άπ—³π—²π˜€π˜π˜†π—Ήπ—².