cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

Timers: setTimeout & setInterval

Schedule JavaScript callbacks with setTimeout and setInterval, cancel them safely, compare repeating patterns, and measure why timer delays are minimums rather than promises.

By the end you can
  • 01
    Schedule and cancelUse setTimeout, setInterval, clearTimeout, and clearInterval safely.
  • 02
    Choose the repeating patternPick setInterval or nested setTimeout based on overlap, drift, and cleanup.
  • 03
    Measure timer honestyExplain clamping, throttling, overflow, and why a delay is only a minimum.

Timers are reminders, not pauses

JavaScript runs one piece of code until that piece is finished. A timer does not freeze the program in place. Instead, a host such as the browser or Node.js keeps a reminder and calls your callback later. That fits the previous lesson’s idea: the host does the waiting, and the callback runs after the current JavaScript has finished.

In this lesson, setTimeout schedules one future callback, setInterval schedules repeated callbacks, and the matching clear... functions cancel work you no longer want. You will measure real delays in your own tab because timing is famously environment-dependent: device speed, tab visibility, battery saving, and page load all matter.

Real-life analogyA timeout is a kitchen timer

Think of setTimeout as setting a kitchen timer. You keep cooking now. Later, the timer rings once. If you decide the dish is cancelled, clearTimeout is turning that timer off before it rings.

In real life: Set a timer for five minutes
In JavaScript: setTimeout(callback, 5000)
In real life: Turn the timer off before it rings
In JavaScript: clearTimeout(id)
In real life: The chef is busy plating food when it dings
In JavaScript: The callback waits until JavaScript is free

Where the analogy stops: A kitchen timer rings by itself. JavaScript callbacks still need the main thread to be available, so the ding can wait behind busy code.

One-line definition

A JavaScript timer asks the host to call a function after at least a delay; it never promises the callback will run at the exact millisecond.

setTimeout & clearTimeout

STEP THROUGH

setTimeout(callback, delay) returns immediately. In browsers it returns a numeric id; in Node.js it returns a Timeout object. Either way, keep that value if you might cancel the timer. The optional values after the delay are passed to the callback as arguments when it runs.

Extra callback argumentsPop out in the code editor (opens in a new tab)JavaScript
setTimeout((name, count) => {
  console.log(name + " has " + count + " reminders");
}, 1000, "Ada", 3);

The source below schedules two timeouts, cancels one, and logs synchronously before the callbacks get a chance. Step through the real order: a timeout with 0 still runs later, not in the middle of the current script.

Step through timer order and cancellation
Step 0 of 8Ready
Your turn: follow the blue line

Predict the order. The timeouts are real browser-style timers, but this player replays the recorded order so you can step through it.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const keep = setTimeout(() => console.log("D: 20ms timeout"), 20);const cancel = setTimeout(() => console.log("never runs"), 10);setTimeout(() => console.log("C: 0ms timeout"), 0);clearTimeout(cancel);console.log("B: end");
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Cancellation is most important during cleanup. If a component resets, unmounts, or navigates away, clear any pending timer so an old callback does not update stale UI.

Timer lab: is 500 ms exactly 500 ms?
Measured timeoutPop out in the code editor (opens in a new tab)JavaScript
const started = performance.now();const id = setTimeout(() => {  const actual = performance.now() - started;  console.log(actual);}, 500); // Optional: block the thread before the callback can run.
Resultmeasured on this device
Not run yetRequested: 500 msBusy work: 0 ms
Try it yourself

Run the lab, then try adding busy work. The measurement uses performance.now() in your browser when you click.

This measures a real timeout in your current tab. Background tabs, battery saving, and CPU load can change the result. Reset clears any pending timer.

setInterval vs nested setTimeout

INTERACTIVE

setInterval(fn, 500) is a metronome: it keeps trying to tick every 500 ms. That is fine for a clock or simple repeated check. But if the work sometimes takes longer than expected, interval callbacks can bunch up or overlap with async work you started inside them.

Real-life analogyMetronome or reset-after-work timer?

An interval is like a metronome. It keeps ticking even if you fall behind. A nested timeout is more like resetting a kitchen timer each time you finish a task, so there is always a full gap after the work.

In real life: A metronome keeps ticking even if you miss a beat
In JavaScript: setInterval keeps scheduling starts
In real life: Finish chopping, then set the next reminder
In JavaScript: Nested setTimeout waits until work is done
In real life: Press Stop on the metronome
In JavaScript: clearInterval(id) or clearTimeout(id) in cleanup

Where the analogy stops: Neither pattern gives hard real-time guarantees. For animation use requestAnimationFrame; for audio use audio-specific scheduling.

Same repeated job, different shapePop out in the code editor (opens in a new tab)JavaScript
setInterval(async () => {
  await saveSlowly();
}, 500);

async function loop() {
  await saveSlowly();
  timeoutId = setTimeout(loop, 500);
}
Drift playground: interval vs nested timeout
Two repeat patternsPop out in the code editor (opens in a new tab)JavaScript
setInterval(tick, 500); function nestedTick() {  doSlowWork(150);  setTimeout(nestedTick, 500);}
Callback startsabout 5 seconds

No ticks yet.

Try it yourself

Start the comparison. It runs for about five seconds, simulates 150 ms of work on every tick, and cleans up all timers on Stop, Reset, or unmount.

Measured on this device. The chart is a teaching visualization of actual callback start times, not a precision benchmark.

The right choice is practical: choose setInterval for regular lightweight ticks; choose nested setTimeout when each run should finish before the next delay starts, or when the next delay changes after every attempt.

setTimeout, setInterval, or nested setTimeout?
  • Hide a toast once, 4 seconds after it appears
  • Update a simple clock display every second
  • Ask a server again only after the previous request finished
  • Save 500 ms after the user stops typing
  • Tick a training metronome until Stop is pressed
  • Retry with a different delay after each failed attempt
Try it yourself
0 of 6 correct

Sort each job by the timer pattern you would reach for first.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Minimum delays & clamping

REAL MEASUREMENTS

A delay of 0 means “as soon as the host is allowed to run this callback,” not “right now.” Browsers also protect the page and the device by enforcing minimum delays in some situations. The HTML spec clamps nested timers to at least 4 ms after five levels of nesting. Your measured numbers may be larger or noisier.

Clamping demo: ten nested 0 ms timers
Nested timeout measurementPop out in the code editor (opens in a new tab)JavaScript
let depth = 0;let last = performance.now(); function next() {  const now = performance.now();  console.log(depth, now - last);  last = now;  depth += 1;  if (depth < 10) setTimeout(next, 0);} setTimeout(next, 0);
Measured gaps0/10

No measurements yet.

Try it yourself

Run ten nested 0 ms timeouts. You are measuring minimum-delay behavior, not guaranteed exact scheduling.

Measured on this device. Background tabs and browser policies can change the numbers. Reset clears the pending nested timer.
Large delays can overflow

Timer delays are handled with 32-bit-ish limits. In Node.js, setTimeout(fn, 2 ** 31) fires almost immediately and emits a TimeoutOverflowWarning. Browsers also apply 32-bit timer handling, so very large delays are not a reliable way to wait for weeks.

Trust issues with setTimeout

MEASURE

The trustworthy sentence is: a timeout waits at least the requested delay. It can be later because the current script is busy, another callback is running, the browser throttles a background tab, or the device is under load. Background browser tabs are commonly throttled to about once per second, and some browsers become more aggressive in certain cases.

This is why every timing demo in this lesson measures with performance.now() after you click. The displayed results are not copied from the author’s machine; they are measured on this device, in this tab, now.

Timer APIs at a glance
APIWhat it schedulesCancel withReturn valueNotes
setTimeout(fn, delay, ...args)Runs once after at least delay msclearTimeout(id)Browser: number. Node.js: Timeout object.Extra arguments are passed to fn when it runs.
setInterval(fn, delay, ...args)Runs repeatedly, trying to start every delay msclearInterval(id)Browser: number. Node.js: Timeout object.Stop it yourself or it keeps scheduling callbacks.
Nested setTimeoutRuns once, then schedules the next run after work finishesStore the latest id and call clearTimeout(id)Same as setTimeoutGreat for polling or avoiding overlap.
Node setImmediate(fn)Node-specific: run after the current poll phaseclearImmediate(id)Node Immediate objectMentioned for Node differences; browsers do not have it.

Where you’ll use timers

Timers show up in user interfaces constantly: hiding a toast, debouncing search input, retrying a request, showing a countdown, and stopping a spinner if something takes too long. Two published lessons connect closely: Closures in loops, timers & memory explains the classic loop variable problem, and Losing this explains why setTimeout(obj.method) can detach a method from its object.

A polling loop that avoids overlapPop out in the code editor (opens in a new tab)JavaScript
let timeoutId;
let stopped = false;

async function poll() {
  await loadLatestData();
  if (!stopped) timeoutId = setTimeout(poll, 1000);
}

poll();

function stopPolling() {
  stopped = true;
  clearTimeout(timeoutId);
}

Notice the cleanup story: stopped prevents scheduling another timeout, and clearTimeout(timeoutId) cancels a waiting one. Real apps put that cleanup in a Reset button, a component unmount effect, or a route-change handler.

Common misconceptions

  • “0 ms means immediately.” It means later, after the current script and only when the host can run the callback.
  • “500 ms means exactly 500 ms.” It means at least 500 ms; busy code and throttling can delay it.
  • “Intervals wait for async work.” They do not know whether your promise finished. Use nested timeouts or explicit guards.
  • “Clearing after a timeout fires changes history.” Clearing only cancels a pending timer. Once a callback ran, it ran.
  • “Huge delays are safe reminders.” Values above 2 ** 31 - 1 ms overflow in Node and are unsafe in browsers too.
  • “Timer ids are the same everywhere.” Browsers use numbers; Node returns objects.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict timer order

Read the code and type the four printed words in order.

Starter codePop out in the code editor (opens in a new tab)JavaScript
console.log("start");
setTimeout(() => console.log("slow"), 20);
setTimeout(() => console.log("fast"), 0);
console.log("end");

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeBuild a cancellable countdown

    Write a countdown that prints 5, 4, 3, 2, 1, 0 with a nested timeout and has a cancel function.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    let timeoutId;
    let remaining = 5;
    
    function tick() {
      // print remaining, then schedule the next tick
    }
    
    function cancelCountdown() {
      // stop the pending tick
    }
      Exercise 3 · PracticeFix overlapping slow work

      Rewrite the interval as a nested timeout so a slow request cannot overlap the next request.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      setInterval(async () => {
        await loadLatestData();
      }, 1000);
        Exercise 4 · ChallengeDebounce a preview

        Change the preview so it updates only after the user stops typing for 300 ms.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        input.addEventListener("input", () => {
          preview.textContent = input.value;
        });
          Exercise 5 · PracticeCancel on Reset

          Predict what prints when Reset-style cleanup clears one timer but leaves another scheduled.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const id = setTimeout(() => console.log("save"), 10);
          clearTimeout(id);
          setTimeout(() => console.log("done"), 0);

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            7 QUESTIONS
            Timers quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does the delay argument to setTimeout(fn, 500) really mean?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this print first, second, and third?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log("A");
              setTimeout(() => console.log("C"), 0);
              console.log("B");

              Choose an answer to see the explanation.

            3. Question 3 of 7Which cancellation pair is correct?

              Choose an answer to see the explanation.

            4. Question 4 of 7Why might nested setTimeout be better than setInterval for polling?

              Choose an answer to see the explanation.

            5. Question 5 of 7What does the HTML timer nesting rule say about many nested setTimeout(0) calls?

              Choose an answer to see the explanation.

            6. Question 6 of 7What usually happens to timers in background browser tabs?

              Choose an answer to see the explanation.

            7. Question 7 of 7In browsers, what does setTimeout return?

              Choose an answer to see the explanation.

            Key takeaways

            • setTimeout schedules one future callback; clearTimeout cancels it before it runs.
            • setInterval repeats until clearInterval; nested setTimeout gives you one full gap after work finishes.
            • Timer delays are minimums. Measure real behavior with performance.now() when precision matters.
            • Nested timers are clamped after several levels, background tabs are throttled, and huge delays can overflow.
            • Always clean up timers on Reset, unmount, navigation, or Stop.

            Final definition: a JavaScript timer is a host-managed reminder that queues a callback later, never an exact pause in the current code.

            Up next: The event loop.

            CompleteFrontend Clear concepts. Working examples.