New to Rust? Grab our free Rust for Beginners eBook Get it free →
setInterval JavaScript: How to run code on a repeating schedule

JavaScript lets you run code on a repeating schedule using setInterval(). Call it with a function and a delay in milliseconds, and that function runs continuously at that interval until you stop it. This article covers the syntax, real examples, how it fits into the event loop, how to stop it with clearInterval, when to choose a recursive setTimeout pattern instead, and the common mistakes that trip up beginners.
What is setInterval in JavaScript?
setInterval() is a built-in global function that repeatedly calls a callback function at a fixed time interval. You give it a function and a delay in milliseconds, and it keeps firing that function over and over until you explicitly stop it. It returns a numeric interval ID that you use later with clearInterval() to cancel execution.
The most common uses are updating clocks, running countdown timers, polling an API at regular intervals and driving simple animations. Unlike setTimeout(), which fires once and stops, setInterval keeps going indefinitely.
setInterval syntax and parameters
setInterval() is available globally in browsers and in Node.js, so no import is required.
setInterval(func, delay, param1, param2, ...paramN);
Parameters:
func– the function to call on each tick. Pass a reference, not a call (greetnotgreet()).delay– time in milliseconds between each call. Defaults to0if omitted.param1, param2, ...paramN– optional arguments forwarded tofuncon each call.
Return value:
A positive integer (the interval ID). Store it in a variable so you can cancel the interval later.
const intervalId = setInterval(myFunction, 1000);
// intervalId is a number like 3 or 17
How setInterval works with the JavaScript event loop
JavaScript runs on a single thread. It can only do one thing at a time, so setInterval does not actually pause the thread. It queues work through the event loop instead.
When you call setInterval(fn, 500), the browser or Node.js timer system tracks the delay outside the main thread. When the time is up, it places fn into the task queue (also called the callback queue or macrotask queue). The event loop checks this queue only when the call stack is empty. If your main code is still running, the queued callback waits.
This has a practical consequence: the delay is a minimum, not a guarantee. If your callback takes 200ms to run and you set a 100ms interval, calls pile up because each callback is already overdue by the time it starts. The browser also imposes a minimum delay of 4ms for intervals that are five or more levels nested inside one another.
Knowing this helps you write better timers and explains why setInterval can drift over time in precision-sensitive use cases.
How to use setInterval (basic examples)
Log a message every second
The simplest use: call a function every 1000 milliseconds.
function greet() {
console.log('Hello world');
}
setInterval(greet, 1000);
greet runs once per second indefinitely. Note the function reference greet is passed without parentheses. Passing greet() would execute it immediately and pass its return value (undefined) to setInterval instead.

Display current time with setInterval
function showTime() {
const now = new Date();
console.log(now.toLocaleTimeString());
}
setInterval(showTime, 1000);
Each second this logs the current local time. The JavaScript Date object gives you the current timestamp, and toLocaleTimeString() formats it into a readable string. You can also use template literals to build the output – see our JavaScript string interpolation guide for clean ways to format time strings.
Build a live counter
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Live Counter</title>
</head>
<body>
<div id="counter">0</div>
<script>
let count = 0;
const counterEl = document.getElementById('counter');
function updateCounter() {
count++;
counterEl.textContent = count;
}
const intervalId = setInterval(updateCounter, 1000);
</script>
</body>
</html>
Every second updateCounter increments count and writes the new value to the DOM. Storing the return value in intervalId means you can stop it later when needed.

Build a digital clock
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Digital Clock</title>
</head>
<body>
<div id="clock"></div>
<script>
function updateClock() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
document.getElementById('clock').textContent = `${hours}:${minutes}:${seconds}`;
}
updateClock(); // run immediately on page load
setInterval(updateClock, 1000);
</script>
</body>
</html>
Calling updateClock() before setInterval ensures the clock shows immediately rather than waiting one second. padStart(2, '0') keeps the format two digits wide so 9 becomes 09.

Passing arguments to setInterval
You can pass extra arguments after the delay and setInterval will forward them to your callback on every tick.
function greet(name, greeting) {
console.log(`${greeting}, ${name}!`);
}
setInterval(greet, 1000, 'Pankaj', 'Hello');
// Logs: Hello, Pankaj! every second
Alternatively, use an arrow function wrapper:
setInterval(() => greet('Pankaj', 'Hello'), 1000);
Both approaches work. The wrapper version gives you more flexibility when arguments need to be computed at call time rather than set once at the start.
How to stop setInterval with clearInterval
setInterval runs forever unless you stop it. Use clearInterval() with the ID it returned.
let count = 0;
function printCount() {
console.log('Count:', count);
count++;
if (count >= 5) {
clearInterval(intervalId);
console.log('Stopped after 5 counts');
}
}
const intervalId = setInterval(printCount, 1000);
Output:
Count: 0
Count: 1
Count: 2
Count: 3
Count: 4
Stopped after 5 counts
You can also stop from outside the callback. A common pattern is attaching clearInterval to a button:
<button id="stop">Stop</button>
<script>
const id = setInterval(() => console.log('tick'), 1000);
document.getElementById('stop').addEventListener('click', () => clearInterval(id));
</script>
One thing worth knowing: setInterval and setTimeout share the same ID pool in most environments. So clearTimeout(intervalId) would technically cancel an interval too, but always match clearInterval with setInterval for clarity.
setInterval vs setTimeout
The difference is straightforward:
setInterval | setTimeout | |
|---|---|---|
| Runs | Repeatedly | Once |
| Use when | You need periodic execution | You need a one-time delay |
| Stopping | clearInterval(id) | clearTimeout(id) |
If you want to pause JavaScript execution for a fixed amount of time once, setTimeout is the right call. If you need something to run every N milliseconds continuously, use setInterval.
Node.js has both and they behave much like the browser, though the implementation is handled by libuv rather than the browser’s timer API. See our Node.js Timers Module guide for how setInterval, setTimeout and setImmediate compare in Node.
setInterval vs recursive setTimeout
setInterval fires the callback every N milliseconds regardless of how long the callback takes to run. This means if your callback takes longer than the interval, calls overlap or queue up back-to-back with no gap.
Recursive setTimeout schedules the next call only after the current one finishes:
// setInterval fires every 100ms regardless of execution time
let i = 1;
setInterval(function () {
doWork(i++);
}, 100);
// Recursive setTimeout: 100ms gap between end of one call and start of the next
let j = 1;
setTimeout(function run() {
doWork(j++);
setTimeout(run, 100);
}, 100);
With setInterval, if doWork takes 150ms, the next call is already overdue and fires right away. With recursive setTimeout, there is always at least 100ms between calls. This matters when you are polling a slow API or running a fetch loop where responses could take variable time. For those cases, recursive setTimeout is the safer pattern. For simple UI updates like a clock or counter where the callback is fast and consistent, setInterval is fine.
Common mistakes and how to fix them
Passing parentheses to the callback
// Wrong: executes greet() immediately, passes undefined to setInterval
setInterval(greet(), 1000);
// Correct: passes the function reference
setInterval(greet, 1000);
If your function needs arguments, use a wrapper arrow function rather than calling it directly:
setInterval(() => greet('Pankaj'), 1000);
Losing this context
Regular functions passed to setInterval run with this set to the global object (window in browsers), not the object the method belongs to:
class Timer {
constructor() {
this.ticks = 0;
}
start() {
// Wrong: `this` is lost inside a regular function
setInterval(function () {
this.ticks++; // TypeError: Cannot set properties of undefined
}, 1000);
}
}
Fix it with an arrow function, which inherits this from the enclosing scope:
start() {
// Correct: arrow function keeps `this` from start()
setInterval(() => {
this.ticks++;
console.log(this.ticks);
}, 1000);
}
Or use .bind():
setInterval(this.tick.bind(this), 1000);
This is a frequently tested concept. Check JavaScript interview questions to see how this and the event loop come up in interviews.
Memory leaks from uncleared intervals
If a component mounts and starts an interval but never clears it on unmount, the callback keeps running and holds references to variables in its closure. In React, always clear intervals in the cleanup function of useEffect:
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(id); // cleanup on unmount
}, []);
The same applies in vanilla JS. Always clear your intervals when you no longer need them. A good rule: every setInterval call should have a corresponding clearInterval somewhere in your code, whether that is inside the callback itself, in a cleanup function, or tied to a user action like a button click.
Practical use cases
setInterval covers a wide range of browser and Node.js scenarios:
- Digital clocks and countdowns – update a time display every second.
- Auto-refreshing dashboards – poll a REST API every 30 seconds to keep metrics current.
- Animations – toggle classes or update canvas frames at a fixed rate. For smooth browser animations,
requestAnimationFrameis the better option butsetIntervalworks for coarser updates. - Auto-advancing carousels – move a slideshow to the next image every few seconds.
- Game loops – update positions, check collisions and re-render simple 2D games.
- Health checks in Node.js – ping a service every minute and log the result.
When working with async operations inside setInterval, like fetch calls, the interval itself is not async-aware. If the fetch is still in flight when the next tick fires, you get concurrent requests. Recursive setTimeout solves this cleanly. For parallel async patterns more broadly, see our guide on running async/await functions in parallel.
Key Takeaways
setInterval(fn, delay)runsfneverydelaymilliseconds until stopped- It returns a numeric ID. Store it so you can call
clearInterval(id)later - Delay is a minimum, not a guarantee. The event loop may push calls later if the stack is busy
- Always call
clearIntervalwhen you no longer need the interval to avoid memory leaks - Pass a function reference, not a function call. Use
greetnotgreet() - Use an arrow function to preserve
thiscontext inside a class or object method - Recursive
setTimeoutguarantees a gap between calls. Prefer it when callbacks are slow or variable
Common setInterval Questions and Answers
What does setInterval do in JavaScript?
It repeatedly calls a function at a fixed time interval in milliseconds and keeps running until you stop it with clearInterval().
What is the difference between setInterval and setTimeout?
setTimeout fires once after a delay. setInterval fires repeatedly at a fixed interval until cancelled.
How do I stop setInterval in JavaScript?
Store the return value from setInterval in a variable and pass it to clearInterval(id) when you want to stop.
Does setInterval run immediately?
No. It waits for the delay before the first call. Call the function once before setInterval if you need immediate execution.
What happens if I don’t clear a setInterval?
The callback keeps running after the page navigates or the component unmounts, causing a memory leak.
Can I pass arguments to the setInterval callback?
Yes. Add them after the delay: setInterval(fn, 1000, arg1, arg2) and fn receives them on each call.
Why is my setInterval not exactly on time?
JavaScript is single-threaded. If the call stack is busy, the event loop delays queued callbacks. The delay parameter is a minimum wait, not a precise timer.


