Suggest an editImprove this articleRefine the answer for “setTimeout and setInterval in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)`setTimeout` runs a callback once after a minimum delay; `setInterval` repeats it on every interval until stopped. ```javascript setTimeout(() => console.log("Once"), 1000); const id = setInterval(() => console.log("Tick"), 1000); clearInterval(id); // stop it ``` **Key:** Both delays are minimums, not guarantees. For async callbacks, chain `setTimeout` instead of using `setInterval` to prevent overlapping calls.Shown above the full answer for quick recall.Answer (EN)Image**setTimeout** schedules a callback once after a minimum delay; **setInterval** calls it repeatedly on every interval tick until you stop it. ## Theory ### TL;DR - `setTimeout` fires once, like a kitchen timer for one pizza. `setInterval` fires forever, like an oven alarm that beeps every 5 minutes until you turn it off. - Main difference: one-shot vs. repeating. - Need to run something once? Use `setTimeout`. Need a loop? Use `setInterval`, but chain `setTimeout` for better control with async work. - Both return an ID you can pass to `clearTimeout` or `clearInterval` to cancel. - The delay is a minimum, not a guarantee. The event loop may add more time. ### Quick example ```javascript // setTimeout: fires ONCE after delay const timeoutId = setTimeout(() => console.log("Fired once"), 100); // clearTimeout(timeoutId); // cancel before it fires // setInterval: fires REPEATEDLY every interval const intervalId = setInterval(() => console.log("Tick"), 1000); setTimeout(() => clearInterval(intervalId), 5000); // stop after ~5 ticks ``` `setTimeout` adds one task to the queue. `setInterval` keeps adding a new task every interval, whether the previous one finished or not. ### Key difference `setInterval` schedules the next call the moment each interval elapses. It does not wait for the callback to finish. If your callback takes 1.5 seconds and the interval is 1 second, calls start queuing up behind each other. `setTimeout` fires once and stops. To get a repeating timer that actually waits for the callback to complete, chain `setTimeout` inside itself. ### When to use - Hide a loading spinner after a delay: `setTimeout`. - Poll an API every 5 seconds: `setInterval` for simple cases, or chained `setTimeout` when the callback is async. - Debounce a search input: chain `setTimeout` with `clearTimeout` on each keystroke. - Smooth 60fps animation: `requestAnimationFrame`, not timers. - Avoid blocking the UI: any timer beats a `while` loop. ### Comparison table | Feature | setTimeout | setInterval | |---|---|---| | Execution | Once after delay | Repeatedly every interval | | Minimum delay | ~4ms (browsers clamp nested calls) | ~4ms, but calls can overlap | | Cancellation | `clearTimeout(id)` | `clearInterval(id)` | | Async safety | Predictable | Can pile up if callback is slow | | When to use | Debounce, one-off delays, cleanup | Clocks, simple polling | ### How the browser handles this `setTimeout` and `setInterval` are not part of the JavaScript engine. They live in the browser's Web API layer (or libuv in Node.js). When the delay elapses, the browser places the callback on the macrotask queue. The [event loop](/questions/event-loop-in-javascript) picks it up only after the current call stack and all microtasks (Promises) are clear. That is why `setTimeout(fn, 0)` does not run immediately. Zero milliseconds is not zero time. Browsers also enforce a minimum ~4ms delay for nested `setTimeout` calls beyond 5 levels deep (HTML5 spec). In background tabs, browsers throttle timers even more, sometimes up to 1000ms. ### Common mistakes **Passing a function call instead of a reference** ```javascript function greet(name) { console.log(`Hi ${name}`); } setTimeout(greet("Alice"), 1000); // runs immediately, passes undefined to setTimeout setTimeout(() => greet("Alice"), 1000); // correct ``` `greet("Alice")` executes right away and returns `undefined`. You are passing that result to `setTimeout`, not the function itself. **Forgetting cleanup in React components** ```javascript // Memory leak: interval survives unmount useEffect(() => { setInterval(() => fetchData(), 5000); }, []); // Correct useEffect(() => { const id = setInterval(fetchData, 5000); return () => clearInterval(id); }, []); ``` The interval keeps running after the component unmounts. The fix is one line: return a cleanup function. **Using setInterval with slow async callbacks** ```javascript // Calls can overlap if fetch takes longer than 5s setInterval(async () => { const data = await fetch("/api/status"); updateUI(data); }, 5000); // Better: wait for each call to finish function poll() { fetch("/api/status") .then(res => res.json()) .then(data => { updateUI(data); setTimeout(poll, 5000); }); } poll(); ``` **Trying to change the interval from inside the callback** ```javascript let delay = 1000; setInterval(() => { delay *= 2; // does nothing - interval was fixed at scheduling }, delay); // If you need dynamic delays, chain setTimeout: let delay = 1000; function tick() { console.log("Tick"); delay *= 2; setTimeout(tick, delay); } setTimeout(tick, delay); ``` ### Real-world usage - React search input: `useEffect` + `setTimeout` for 300ms debounce, `clearTimeout` in cleanup. - Redux DevTools: `setInterval` for periodic state snapshots. - VS Code: chained `setTimeout` for typing debounce in autocomplete. - Express/Node.js: graceful shutdown timers, `setTimeout` to force-close after a cleanup window. - For animations use `requestAnimationFrame`; for real-time data use WebSockets instead of polling. ### Follow-up questions **Q:** Why does `setTimeout(fn, 0)` not run immediately? **A:** It pushes the callback to the macrotask queue. The event loop runs all sync code and microtasks first, then picks it up. Zero delay means "as soon as possible after current work", not "right now". **Q:** What is the ~4ms clamp? **A:** The HTML5 spec throttles nested `setTimeout` calls (after 5 levels) to a minimum of 4ms. Background tabs can be throttled to 1000ms or more to save CPU. **Q:** When should you prefer chained `setTimeout` over `setInterval`? **A:** Any time the callback is async or takes variable time. Chaining guarantees the next call starts only after the previous one finishes, so calls never overlap. **Q:** How do you cancel `setInterval` safely when the ID is captured in a closure? **A:** Store the ID in a variable outside the callback: `const id = setInterval(...); setTimeout(() => clearInterval(id), 5000)`. Never rely on the callback to store its own ID. **Q:** In a tab where tasks take 100ms each, what happens to `setInterval(..., 10)`? **A:** Callbacks queue up but do not run in parallel since JS is single-threaded. The event loop processes them one by one, so actual intervals end up around 100ms, not 10ms. You get a backlog, not concurrency. ## Examples ### Basic: deferred single action ```javascript console.log("Start"); const id = setTimeout(() => console.log("Fired"), 100); console.log("End"); // Output: Start -> End -> Fired // "End" prints before "Fired" because setTimeout is async ``` `setTimeout` does not block. The engine moves on to `console.log("End")` immediately, and fires the callback later from the macrotask queue. ### Intermediate: debounced search input in React ```javascript function SearchInput() { const [query, setQuery] = useState(""); useEffect(() => { const id = setTimeout(() => { if (query) fetchResults(query); }, 300); return () => clearTimeout(id); // cancel if user keeps typing }, [query]); return <input onChange={e => setQuery(e.target.value)} />; } // Each keystroke resets the timer. // The API call fires only after 300ms of silence. ``` Every time `query` changes, the old timer is cancelled and a new one starts. The request goes out only when the user pauses. ### Advanced: chained setTimeout vs setInterval for API polling ```javascript // setInterval version - can overlap if fetch is slow const id = setInterval(async () => { const res = await fetch("/api/health"); const data = await res.json(); console.log(data.status); }, 2000); // Chained setTimeout - waits for each response function pollHealth() { fetch("/api/health") .then(r => r.json()) .then(data => { console.log(data.status); setTimeout(pollHealth, 2000); // next poll after this one completes }) .catch(() => setTimeout(pollHealth, 5000)); // back off on error } pollHealth(); ``` The chained version lets you adjust the delay based on the result and handle errors cleanly. I have seen the `setInterval` version cause double-requests in production when servers get slow under load. One call was still in flight when the next one fired.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.