Explain setTimeout, setInterval, and clearTimeout
JavaScript timers let you schedule code to run later or repeatedly. They're essential for tasks like animations, polling a server or debouncing user input. The language provides four related functions: setTimeout(), setInterval(), clearTimeout() and clearInterval(). Understanding how they work and when to use each will help you write responsive programs.
setTimeout(fn, delay[, ...args])
setTimeout() sets a one-off timer. It takes a callback function, a delay in milliseconds and optional arguments to pass to the callback. After at least delay milliseconds have elapsed and the current call stack is empty, the callback runs. The call returns a numeric timer ID which you can pass to clearTimeout() to cancel the timer. If delay is omitted or coerced to 0, the callback is scheduled as soon as possible but still runs after all pending microtasks and the current macrotask.
const id = setTimeout(() => {
console.log("Hello after 1s");
}, 1000);
// Cancel the timeout before it runs:
clearTimeout(id);
Notes:
JavaScript runs tasks one at a time using something called the "event loop." When you use setTimeout() to schedule a function, you're asking the browser to run that function after a certain number of milliseconds. However, this waiting time isn't an exact guarantee. If other code is already running or if the browser is busy handling things like user events or rendering the page, your timer's callback will wait its turn and may fire later than you requested.
Browsers also set a lower limit on how frequently timers can fire when they're nested deeply. After you schedule a few timers inside one another (beyond about five levels of nesting), most browsers won't let them run faster than roughly every 4 milliseconds. This "clamping" prevents runaway loops from bogging down the tab. In practice, it means that when you repeatedly schedule one setTimeout() from inside another, the callbacks might not run back-to-back; the browser will slow them down slightly to keep the page responsive.
The callback runs asynchronously and does not block subsequent code execution.
setInterval(fn, delay[, ...args])
setInterval() repeatedly calls a function with a fixed delay between calls. It returns an interval ID. The callback continues to run until you call clearInterval(id). Interval IDs share the same pool as timeout IDs; you can technically clear an interval with clearTimeout(), but it's better practice to match functions.
let count = 0;
const intervalId = setInterval(() => {
console.log("Tick", ++count);
if (count === 5) {
clearInterval(intervalId); // stop after 5 ticks
}
}, 1000);
Important considerations:
When you use setInterval() to run a function repeatedly, the browser tries to call that function every X milliseconds regardless of how long the function itself takes to finish. If your function takes longer to run than the interval you've set, the browser can end up stacking multiple calls on top of each other, it might start the next call before the previous one has finished. This can slow your page or create unexpected overlaps. A common workaround is to use setTimeout() inside your function to schedule the next run only after it has completed. That way, each run waits for the previous one to finish before starting the timer again, so nothing overlaps.
Browsers also have built-in limits to prevent timers from running too rapidly. If you have a chain of timers (or intervals) nested more than a few levels deep, most browsers will automatically slow them down so they don't fire more often than about once every four milliseconds. This "throttling" helps keep the page responsive by avoiding a flood of very fast, back-to-back timer events.
Cancelling timers: clearTimeout() and clearInterval()
clearTimeout() and clearInterval() stop scheduled timers. Both functions accept the numeric ID returned by setTimeout or setInterval and cancel any pending execution. If the ID is invalid, nothing happens. Timer IDs are stored on the global window object, so clearing an interval with clearTimeout() technically works, but for readability you should use the matching clear function.
// Schedule and then cancel a one-off timer
const timeoutId = setTimeout(doSomething, 5000);
clearTimeout(timeoutId);
// Schedule and then cancel a repeating timer
const intervalId = setInterval(doSomethingElse, 1000);
clearInterval(intervalId);
Real-world analogy
Think of setTimeout as setting a single alarm clock: you set it for the future, go about your day, and when it rings you perform the task. setInterval is like a repeating alarm that keeps ringing every morning until you switch it off. The IDs returned are like the alarm handles; calling clearTimeout or clearInterval is pressing the off button.
Summary
setTimeoutschedules a callback to run once after a delay. It returns an ID used to cancel the
timer.setIntervalschedules a callback to run repeatedly with a fixed delay between executions and
returns an ID. UseclearIntervalto stop it.- For better control over periodic tasks (especially when the task itself takes time), prefer a recursive
setTimeoutoversetInterval. clearTimeoutandclearIntervalcancel timers. Passing an invalid ID does nothing.- Timers execute asynchronously; even a delay of 0 ms doesn't make the callback synchronous.
Common misconceptions
- "
setTimeout(fn, 0)runs immediately." A zero delay still defers execution until after the
current call stack and microtasks are finished. - "
setIntervalis always precise." Intervals don't compensate for execution time; if the
callback takes longer than the interval, calls can overlap or drift. Use recursive timeouts when
accuracy matters. - "You can only cancel a timeout with
clearTimeout." While timeouts and intervals share an
internal ID pool, mixing clear functions works but is bad practice.
Practice questions
Theory: Why might repeated calls with
setIntervaldrift over time? How does recursivesetTimeoutalleviate this?Coding: Write a function
delay(ms)that returns a promise and resolves afterms
milliseconds usingsetTimeout.
function delay(ms){
return new Promise((resolve) => {
setTimeout(() => {
resolve(ms);
}, ms);
});
};
delay(200);
- Coding: Implement a countdown from 10 to 0 that prints a number every second and then
prints "go!" using a timer. Include a button to cancel the countdown withclearInterval.
function printTill10() {
let count = 0;
let timer = setInterval(() => {
console.log(count);
count ++;
if(count > 10) {
clearInterval(timer);
}
}, 1000);
};
printTill10();