SKU // 47

JavaScript Timers

Timers let you run a function later, or over and over on a schedule. They power countdowns, auto-saving, slideshows, notifications that fade out, and simple animations. Every timer takes a function to run, which makes timers the perfect place to use the callbacks and arrow functions from the last two lessons.

Function What it does
setTimeout(fn, ms)Run a function once, after a delay. Returns an ID
clearTimeout(id)Cancel a timeout that has not fired yet
setInterval(fn, ms)Run a function repeatedly, every delay. Returns an ID
clearInterval(id)Stop a repeating interval

// setTimeout: Run Once, Later

setTimeout(function, milliseconds);

The first argument is the function to run; the second is the delay in milliseconds. 1000 ms is one second.

function showMessage() {
  console.log("Three seconds later");
}
setTimeout(showMessage, 3000);

// Or pass an anonymous function, often an arrow:
setTimeout(() => {
  console.log("Two seconds later");
}, 2000);
Two seconds later (after 2 s) Three seconds later (after 3 s)
Watch out: pass the function, do not call it
setTimeout(showMessage, 3000) is correct. setTimeout(showMessage(), 3000) runs showMessage right away and hands the timer its return value instead. Same rule as addEventListener from Function Invocation.

// Timers Do Not Pause Your Code

This surprises almost everyone. Calling setTimeout does not stop JavaScript and wait. It schedules the function for later and immediately carries on with the next line:

console.log("Start");
setTimeout(() => console.log("Timer"), 3000);
console.log("End");
Start End Timer (three seconds later)

Even a delay of 0 does not mean "right now". It means "as soon as the current code has finished":

console.log("first");
setTimeout(() => console.log("third"), 0);
console.log("second");
first second third

// The Delay Is a Minimum

The delay is the earliest the function can run, not a promise. Remember from JS Control Flow that JavaScript is single-threaded: it does one thing at a time. A timer cannot interrupt code that is already running, so if JavaScript is busy, the timer has to wait:

setTimeout(() => console.log("Timer finished"), 1000);

let i = 4e9;
while (--i > 0);     // a busy loop that takes several seconds

The timer was set for one second, but "Timer finished" cannot appear until the loop is done, several seconds later, and the page is frozen the whole time. Browsers also slow timers down in background tabs to save battery.

Note
The same applies to the function a timer runs: a timer only controls when the function starts. If the function itself does something slow, it will freeze the page while it runs, just like any other code.

// Passing Arguments

Extra arguments after the delay are passed to the callback. Or wrap the call in an arrow function, which many people find clearer:

function remind(task, who) {
  console.log(`${who}: time to ${task}`);
}

setTimeout(remind, 1000, "stretch", "Alex");
setTimeout(() => remind("drink water", "Alex"), 2000);
Alex: time to stretch (after 1 s) Alex: time to drink water (after 2 s)

// clearTimeout: Cancel It

setTimeout returns an ID number. Keep it, and you can pass it to clearTimeout to cancel the timer before it fires:

const id = setTimeout(() => console.log("Self-destruct!"), 5000);
clearTimeout(id);
console.log("Crisis averted");
Crisis averted

On a web page, the start and cancel are usually separate buttons, so the ID lives in a variable both functions can reach:

<button onclick="startTimer()">Start Timer</button>
<button onclick="stopTimer()">Stop Timer</button>
<p id="demo"></p>

<script>
let timer;

function startTimer() {
  timer = setTimeout(() => {
    document.getElementById("demo").innerHTML = "Finished";
  }, 5000);
}

function stopTimer() {
  clearTimeout(timer);
  document.getElementById("demo").innerHTML = "Timer stopped";
}
</script>

// setInterval: Run Repeatedly

setInterval calls the function every time the delay passes, until you stop it:

let seconds = 0;
const timer = setInterval(() => {
  seconds++;
  console.log(`${seconds}s`);
}, 1000);
1s 2s 3s ... (forever, until stopped)

// clearInterval: Stop It

An interval never stops on its own; it keeps going until you clear it or the page closes. Always keep its ID so you can end it:

let count = 3;
const countdown = setInterval(() => {
  console.log(count);
  count--;
  if (count < 0) {
    clearInterval(countdown);
    console.log("Liftoff!");
  }
}, 1000);
3 2 1 0 Liftoff!
Watch out: starting several intervals
If you start an interval inside a function that can run more than once (like a button's click handler), each click starts another interval, and they all run at the same time: a clock that ticks twice as fast, then three times as fast. Only start one if none is running, and reset the ID when you stop it:
let clock;

function startClock() {
  if (!clock) {                         // only if not already running
    clock = setInterval(showTime, 1000);
  }
}

function stopClock() {
  clearInterval(clock);
  clock = undefined;                  // so start works again
}

// Repeating with setTimeout

There is a second way to repeat: a function that runs, then schedules itself again with setTimeout:

function repeat() {
  console.log("Hello");
  setTimeout(repeat, 1000);    // schedule the next run
}
repeat();

The difference is subtle but useful. With setInterval, the next run is scheduled at fixed intervals no matter how long each run takes. With repeated setTimeout, the next delay only begins after the current run has finished. Use it when one task must be completely done before waiting for the next, like checking a server for new messages.

// A Real Example: A Page Clock

This updates an element on the page every second. It needs a <p id="clock"></p> in the HTML:

function updateClock() {
  const now = new Date();
  document.getElementById("clock").innerHTML = now.toLocaleTimeString();
}

updateClock();                    // show it immediately
setInterval(updateClock, 1000);   // then refresh every second
Tip
Calling the function once before starting the interval means the clock appears right away, instead of being blank for the first second. (new Date() is the current date and time; JS Dates covers it.)

// Never Pass Code as a String

For historical reasons, timers also accept a string of code: setTimeout("myFunction()", 1000). It works, but do not use it. The string is turned into code at run time, which is slower, hides typos until the moment it runs, and can be a security risk if any part of the string came from a user. Always pass a function: setTimeout(myFunction, 1000).

// Common Mistakes

// Quick Check

What order are these logged in?

console.log("A");
setTimeout(() => console.log("B"), 0);
console.log("C");
Show answer
A, C, B. Even with a 0 ms delay, the timer's function waits until the current code has finished.

Which line runs save every 30 seconds?

  1. setTimeout(save, 30)
  2. setInterval(save(), 30000)
  3. setInterval(save, 30000)
  4. setInterval(30000, save)
Show answer
C. A runs once, after 30 milliseconds. B calls save immediately and passes its result. D has the arguments in the wrong order.

A user clicks "Start" three times, and the clock starts ticking three times per second. What is the fix?

Show answer
Each click started a new interval. Guard the start: only call setInterval if no timer is running (if (!clock)), and set the variable back to undefined when stopping.

// Try It

  1. Click Open Playground. Add a 0 ms timeout that logs "B" between the two logs. Predict the order before you click Run
  2. Start a 5-second timeout, then cancel it with clearTimeout before it fires
  3. Build a countdown from 5 that prints "Go!" and stops itself
  4. Write an interval that logs a random number every half second and stops after 6 numbers
// Video Walkthroughs
Last updated: Oct 10, 2026, 12:52 AM ET