Timers: setTimeout & setInterval
Timers schedule code to run later: once after a delay with setTimeout, or repeatedly with setInterval. They power countdowns, auto-saving, polling, notifications that fade away, and the debounce and throttle techniques that keep busy pages responsive.
setTimeout
console.log("Ordering…"); const id = setTimeout(() => { console.log("Your coffee is ready"); }, 100); console.log("You can do other things while you wait");
Ordering… You can do other things while you wait Your coffee is ready
setTimeout(callback, ms) runs the callback once, after at least ms milliseconds, and returns an ID. The code after it doesn't wait — that's what makes timers asynchronous. (In browsers the ID is a number; in Node.js it's an object, though it works the same way.)
Extra arguments are passed to the callback: setTimeout(greet, 1000, "Ana") calls greet("Ana").
Cancelling with clearTimeout
const reminder = setTimeout(() => console.log("This never prints"), 100); clearTimeout(reminder); console.log("Reminder cancelled");
Reminder cancelled
setInterval
setInterval runs a callback repeatedly, every ms milliseconds, until you stop it with clearInterval:
let remaining = 3; const timer = setInterval(() => { console.log(remaining); remaining--; if (remaining === 0) { clearInterval(timer); console.log("Liftoff!"); } }, 50);
3 2 1 Liftoff!
A live countdown
Notice the countdown computes the time remaining from a fixed end time rather than counting ticks. Timer delays are a minimum, not a guarantee — browsers slow timers in background tabs, and a busy page delays them — so counting ticks drifts.
Timers don't pause your code
Even a delay of 0 waits until the current code finishes:
setTimeout(() => console.log("timeout 0"), 0); for (let i = 0; i < 3; i++) console.log("loop", i);
loop 0 loop 1 loop 2 timeout 0
Lesson 51 explains why: timer callbacks wait in a queue until the call stack is empty.
Recursive setTimeout
An alternative to setInterval is a setTimeout that schedules the next run when it finishes. This guarantees a gap between runs, even if each run takes a while — ideal for polling a server:
let checks = 0; function poll() { checks++; console.log(`check #${checks}`); if (checks < 3) setTimeout(poll, 50); } poll();
check #1 check #2 check #3
Debounce
A search box shouldn't send a request on every keystroke. Debouncing waits until the user stops typing for a moment:
Every keystroke cancels the pending timer and starts a new one, so the search only runs once typing pauses for 400 ms. This is a closure in action (lesson 26).
Throttle
Throttling limits a function to run at most once per time window — useful for scroll and resize handlers that would otherwise fire dozens of times a second:
function throttle(fn, wait) { let last = 0; return (...args) => { const now = Date.now(); if (now - last >= wait) { last = now; fn(...args); } }; } let runs = 0; const onScroll = throttle(() => runs++, 1000); for (let i = 0; i < 100; i++) onScroll(); console.log(`called 100 times, ran ${runs} time(s)`);
called 100 times, ran 1 time(s)
requestAnimationFrame
For animations, use requestAnimationFrame(callback) instead of timers. It runs right before the browser paints the next frame (usually 60 times a second), pauses in background tabs, and produces smoother motion: