Skip to main content

Command Palette

Search for a command to run...

Lesson 53: Mastering JavaScript setTimeout and setInterval with challenges!

Updated
5 min read
  • setTimeout(fn, delay) → Runs fn once after delay milliseconds.

  • setInterval(fn, delay) → Runs fn repeatedly, every delay milliseconds.

Both are asynchronous and macrotask-based, meaning they are queued after the current synchronous code and all microtasks complete.


🧪 Examples

✅ Basic: One-time execution

setTimeout(() => console.log("Fired after 1s"), 1000);

✅ Basic: Repeated execution

let id = setInterval(() => console.log("Tick"), 2000);
setTimeout(() => clearInterval(id), 7000); // stops after 3 ticks

✅ With arguments

function greet(name) {
  console.log(`Hello, ${name}`);
}
setTimeout(greet, 1000, "Alice");

❌ Common mistake: invoking instead of referencing

etTimeout(greet("Bob"), 1000); // ❌ Runs immediately

✅ Correct:

setTimeout(() => greet("Bob"), 1000);

📊 Diagram: setTimeout Execution Flow

main()
 ↓
queueMicrotask → resolved promises
 ↓
queueMacrotask → setTimeout/setInterval
 ↓
event loop picks the next task

🔹 2. Fill Any Gaps (Advanced Mechanics, Edge Cases, Internals)

🔍 Internals of Timer Handling

  • Timers aren't part of the JavaScript engine (e.g. V8) — they are provided by the host environment:

    • Browsers: via the HTML Living Standard timers.

    • Node.js: via libuv.

🔄 setInterval vs Nested setTimeout

  • setInterval tries to run at every interval regardless of previous task duration.

  • Nested setTimeout allows adaptive scheduling and ensures a fixed gap between executions.

🔁 Recommended: Use nested setTimeout for:

  • Animations

  • Recursive polling

  • Adaptive delays

⚠️ Timer Clamping (Minimum Delay)

Browser Quirk:

  • After 5+ nested setTimeout(fn, 0), browser forces a 4ms delay.

  • In background tabs, delays are throttled to ~1000ms.

🧯 Memory + GC Behavior

  • A function scheduled with setInterval is retained in memory until clearInterval() is called.

  • Can leak memory due to closure capturing outer lexical scope.

🧵 setTimeout is a macrotask, not a microtask

Promise.resolve().then(() => console.log("microtask"));
setTimeout(() => console.log("macrotask"), 0);

// Output:
// microtask
// macrotask

🔹 3. Challenge Me Deeply (10 problems + 1 brain twister)

🟢 Basic

  1. Schedule a log that prints "First!" after 3 seconds.

  2. Use setTimeout to print numbers 1 to 5, each after i * 1000 ms delay.

  3. What's wrong with this?

     setTimeout(console.log("Done!"), 2000);
    

🟡 Intermediate

  1. Write a function repeat(fn, times, delay) that runs fn times with delay between runs.

  2. Simulate setInterval using setTimeout recursively.

  3. Write a countdown timer from 10 to 0 using setInterval.

🔴 Advanced

  1. You have a polling function poll() that should retry after 2s, then 4s, 8s... (exponential backoff). Implement it.

  2. Write a scheduler runAfter(func, ms) that ensures func runs only after all pending promises are settled.

  3. Create a debouncer using setTimeout that delays function execution until there's a pause in user input.

  4. Build a simplified version of setImmediate(fn) in the browser using setTimeout, MessageChannel, or postMessage.

🎯 Brain-twister

  1. What is the order of output here?
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);

🔹 4. Interview-Ready Questions

📚 Conceptual

  • What’s the difference between setTimeout(fn, 0) and queueMicrotask(fn)?

  • When would you prefer nested setTimeout over setInterval?

🔎 Scenario-Based

  • You implemented polling with setInterval and noticed overlapping requests. Why?

  • You use setTimeout(..., 0) in a tight loop and get inconsistent timing. Why?

🐞 Debugging

  • A setInterval runs infinitely even after your component is destroyed. What’s likely wrong?

  • A delayed function closes over stale variables — what might be the cause?

✅ Best Practices

  • ✅ Always clear setInterval in cleanup hooks (React, Vue).

  • ✅ Prefer arrow functions or named references; avoid passing strings.

  • ❌ Avoid setTimeout(..., 0) to force immediate execution — use Promise.resolve().then() instead for microtask timing.


🔹 5. Real-World Usage

🔧 Frontend:

  • Debounce and throttle in search bars, scroll events, resize listeners

  • Scheduling retries in HTTP polling (e.g. REST API, GraphQL)

  • Timer-based animations (game loops, loaders)

💡 Frameworks:

  • React: setTimeout in useEffect, useRef for cancellation

  • Node.js: setTimeout, setImmediate, process.nextTick

  • RxJS: Timers in reactive streams (interval, timer)

🧩 Example:

// React auto-save draft after user stops typing for 2s
const [text, setText] = useState("");
useEffect(() => {
  const id = setTimeout(() => saveDraft(text), 2000);
  return () => clearTimeout(id); // cleanup
}, [text]);

🔹 6. Remember Like a Pro

🧠 Mnemonics:

  • T in setTimeout → Think Temporary (once)

  • I in setInterval → Think Infinite (repeats)

🗺️ Visual Summary (Cheatsheet)

FeaturesetTimeoutsetInterval
Run Once?
Repeats?
Cancel MethodclearTimeout(id)clearInterval(id)
Flexible Delay?✅ (nested style)
GC risk?Low (runs once)High (runs forever)
Timing accuracyHighLower (cumulative lag)

🔹 7. Apply It in a Fun Way

🧪 Mini Project: Countdown Timer + Stop Button

🧩 Goal:

Create a countdown timer that:

  • Starts from 10

  • Updates DOM every second

  • Has a Stop button

🔨 Steps:

  1. Create a button and a display <div>.

  2. On clicking start, run a countdown from 10 using setInterval.

  3. Each second, update the DOM.

  4. If stop button is pressed, call clearInterval.

<button id="start">Start</button>
<button id="stop">Stop</button>
<div id="timer">10</div>
let timerId;
document.getElementById("start").onclick = () => {
  let count = 10;
  document.getElementById("timer").textContent = count;
  timerId = setInterval(() => {
    count--;
    document.getElementById("timer").textContent = count;
    if (count === 0) clearInterval(timerId);
  }, 1000);
};

document.getElementById("stop").onclick = () => clearInterval(timerId);

➕ Extensions:

  • Add a "Pause" button

  • Display elapsed time

  • Save state in localStorage


🧠 Optional – Extra Value

🛠️ Open Source Projects

  • React: uses timers internally for animations, suspense, and effects

  • Axios: timers for timeout and retries

  • Lodash: _.debounce, _.throttle use setTimeout

🚨 Mistakes Devs Make

  • Forgetting to cancel setInterval → memory leaks

  • Using setTimeout(..., 0) instead of Promise.resolve().then()

  • Assuming timers run precisely (they don’t under load or background tabs)

  • Passing string code → potential security holes (like eval)

⚡ Performance Tips

  • Batch DOM updates with requestAnimationFrame if timing animation

  • Cancel unnecessary timers in cleanup (especially in SPA frameworks)