setInterval()

Runs a function repeatedly at a fixed minimum interval until you cancel it with clearInterval().

Signature
setInterval(func)
setInterval(func, delay)
setInterval(func, delay, param1)
setInterval(func, delay, param1, param2)
setInterval(func, delay, param1, param2, /* …, */ paramN)

setInterval(code)
setInterval(code, delay)
  • Added in JavaScript Baseline widely available since July 2015

Parameters

ParameterTypeDescription
$funcFunctionThe function to execute every delay milliseconds. The first execution happens after the first delay, not immediately.
$codestring | TrustedScriptAn alternative to func: a string of source compiled and run on every tick. It carries the same injection risk as eval() and is strongly discouraged.
$delayoptionalnumber= 0Milliseconds to wait between executions. Omitted or 0 means the callback is re-queued as fast as the event loop allows, subject to clamping.
$param1, …, paramNoptionalanyAdditional values passed through as arguments to func on every tick.

Return value

numberA positive integer interval ID that uniquely identifies the timer in the current global scope; pass it to clearInterval() to stop it.

Errors

Throws a SyntaxError if a string code argument cannot be parsed, and a TypeError if the first argument is not a function, string or TrustedScript, or if Trusted Types are enforced by CSP and a string is passed with no default policy.

Changelog

  • PHP HTML Living StandardAfter five levels of nesting, the delay is clamped to a minimum of 4 ms.
  • PHP HTML Living Standarddelay is stored as a signed 32-bit integer, so values above 2,147,483,647 ms (about 24.8 days) overflow.

setInterval() runs a callback over and over, waiting at least delay milliseconds between runs, and keeps going until something calls clearInterval() with the returned ID. The first run happens after the first delay, so nothing occurs at time zero — call the function yourself first if you need an immediate tick.

When to reach for it

Cheap, fixed-cadence work: updating a clock, counting down, animating a progress readout, or polling a value that is already in memory. It is a good fit when each tick is fast and it does not matter much if one drifts.

What trips people up

The interval does not wait for your callback. If the work takes longer than the delay — an async request, a heavy loop — the runtime keeps re-queuing, and once the queue catches up the invocations run back to back with no gap. That is why anything slow or network-bound should use a self-rescheduling setTimeout() loop instead, which only schedules the next run after the current one finishes.

Also expect drift: ticks are never exactly delay apart, so never accumulate elapsed time by counting ticks — read the clock. Nested timers clamp to a 4 ms minimum, background tabs throttle intervals to a second or more, and an interval whose ID you lost runs until the page is unloaded.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Updating a visible clock or countdown once a second
  • Polling an in-memory flag or queue until a condition is met
  • Driving a simple animation or progress readout at a fixed cadence
  • Emitting a heartbeat while a long-running job is in flight

Frequently asked questions

Why does setInterval drift and not fire exactly on time?

Each tick is only queued once the previous delay has elapsed and can only run when the call stack is empty, so scheduling overhead accumulates. Nested-timer clamping to 4 ms and background-tab throttling make it worse, which is why elapsed time should always come from Date.now() rather than counting ticks.

How do I run an async function on an interval?

Prefer a self-rescheduling setTimeout loop that calls itself after the awaited work resolves, because setInterval does not wait for the previous callback and overlapping runs will pile up. If you must use setInterval, guard the body with an "in flight" flag that returns early while the previous run is still pending.

How do I make setInterval run immediately instead of waiting?

Call the function once yourself and then start the interval, since the first tick always comes after the first full delay. A common pattern is const tick = () => {...}; tick(); const id = setInterval(tick, 1000).

More examples

A countdown with a forwarded argument

Everything after the delay is forwarded to the callback on every tick, so "T-minus" arrives as label each time without a closure. The counter reaching zero is what stops the timer — the interval itself has no built-in run limit.

Example · javascript
let remaining = 3;

const id = setInterval((label) => {
  console.log(`${label}: ${remaining}`);
  if (--remaining === 0) {
    clearInterval(id);
    console.log("liftoff");
  }
}, 15, "T-minus");

await new Promise((r) => setTimeout(r, 150));

When the work is slower than the interval

Each run takes 30 ms but only asks for a 10 ms gap. Because the next timer is scheduled from inside the callback, the runs land roughly 40 ms apart with real breathing room — with setInterval they would pile up and execute back to back.

Example · javascript
const start = Date.now();
let runs = 0;

// Self-rescheduling: the next run is only queued once this one finishes.
(function loop() {
  setTimeout(() => {
    const busyUntil = Date.now() + 30;   // work longer than the gap
    while (Date.now() < busyUntil) {}

    console.log(`run ${++runs} done at ~${Date.now() - start}ms`);
    if (runs < 3) loop();
  }, 10);
})();

await new Promise((r) => setTimeout(r, 250));

Intervals drift, so read the clock

Multiplying the tick count by the delay always understates the real elapsed time, because each tick carries scheduling overhead. Any timer, stopwatch or progress bar should derive elapsed time from Date.now() rather than counting ticks.

Example · javascript
const start = Date.now();
let ticks = 0;

const id = setInterval(() => {
  ticks += 1;
  if (ticks === 5) {
    clearInterval(id);
    const assumed = ticks * 20;
    const actual = Date.now() - start;
    console.log("ticks * delay says:", assumed, "ms");
    console.log("the clock says:", actual, "ms");
    console.log("drift:", actual - assumed, "ms");
  }
}, 20);

await new Promise((r) => setTimeout(r, 300));

Discussion

  • Be the first to comment on this lesson.