Skip to content

Intermediate · JavaScript in the browser: DOM and events · topic 38 of 64 · 25 min

Timers: running code later

Explanation

setTimeout runs a function once after a delay, and setInterval runs it again and again. You stop them with clearTimeout and clearInterval. With these you can build an OTP resend countdown or a debounced search box, but remember that timers are never exactly on time.

Running code later with setTimeout

setTimeout(fn, ms) asks the browser to run the function fn once, after at least ms milliseconds. 1000 milliseconds is 1 second. It returns an id number. Pass that id to clearTimeout(id) to cancel it before it runs.

Think of it like this: Think of setting an alarm on your phone. setTimeout is a one-time alarm: "wake me in 10 minutes". setInterval is a repeating alarm: "remind me every hour". clearTimeout and clearInterval are the button that switches the alarm off.
javascript
const toast = document.querySelector('#toast');
toast.textContent = 'Added to cart';
toast.hidden = false;

const timerId = setTimeout(() => {
  toast.hidden = true; // hide the message after 3 seconds
}, 3000);

// if the user closes it early:
document.querySelector('#toast-close').addEventListener('click', () => {
  clearTimeout(timerId);
  toast.hidden = true;
});
A small message that hides itself after 3 seconds

Repeating with setInterval

setInterval(fn, ms) runs fn again and again, every ms milliseconds, until you call clearInterval(id). A common use is a countdown, such as the "Resend OTP in 30s" text on a login page.

javascript
const resendBtn = document.querySelector('#resend');
let secondsLeft = 30;
resendBtn.disabled = true;
resendBtn.textContent = `Resend OTP in ${secondsLeft}s`;

const intervalId = setInterval(() => {
  secondsLeft -= 1;
  resendBtn.textContent = `Resend OTP in ${secondsLeft}s`;
  if (secondsLeft === 0) {
    clearInterval(intervalId); // stop the repeating timer
    resendBtn.disabled = false;
    resendBtn.textContent = 'Resend OTP';
  }
}, 1000);
An OTP resend countdown that stops itself at 0
Careful: Always stop an interval when its job is done. A forgotten setInterval keeps running in the background, wastes battery, and can show strange values such as "-5s".
ToolRunsStop it with
setTimeout(fn, ms)Once, after the delayclearTimeout(id)
setInterval(fn, ms)Again and again, every msclearInterval(id)
requestAnimationFrame(fn)Once, just before the next screen paintcancelAnimationFrame(id)

Debounce: wait until the user stops typing

A search box fires an input event for every letter. If you call the server for every letter of "basmati rice", that is 12 requests. Debounce means: wait until the user has stopped typing for a short time (for example 300 ms), then run once with the latest value.

javascript
function debounce(fn, wait) {
  let timerId;
  return (...args) => {
    clearTimeout(timerId);                          // cancel the old plan
    timerId = setTimeout(() => fn(...args), wait);  // make a new plan
  };
}

const search = document.querySelector('#search');
const searchProducts = debounce((text) => {
  console.log('Searching for', text);
}, 300);
search.addEventListener('input', () => searchProducts(search.value));
Each new letter cancels the previous timer, so only the last one runs

Smooth animation: requestAnimationFrame

For animation in JavaScript, use requestAnimationFrame(fn) instead of a fast setInterval. The browser calls fn just before it paints the next frame (usually 60 times a second), and pauses it when the tab is hidden. For simple effects, CSS transitions are even easier.

Why timers are not exact

A delay is a minimum, not a promise. JavaScript in a page does one thing at a time. If your code is busy with a long loop, the timer waits until it finishes. Browsers also slow down timers in background tabs to save battery, sometimes to once a minute.

Tip: When the real time matters, save the start time with Date.now() and calculate the time left from it, instead of counting ticks. Then the countdown is right even if some ticks come late.

Watch the lesson

Timers: running code later · 1/7

Run code later

Sometimes code should run later, not now. The browser gives us timers for that.

Prefer a video by a teacher? Find free videos on “javascript settimeout setinterval debounce tutorial” on YouTube ↗

In real life: OTP login on a payment app

On a UPI or banking app login page, after you ask for an OTP, the "Resend OTP" button is disabled and shows "Resend OTP in 30s", counting down every second with setInterval. At 0 the interval is cleared and the button turns on. On the same app, the search box for contacts uses debounce, so it searches only when you pause typing.

Practice (0/3 done)

Task 1 · Code · Javascript

Write a function formatTime(seconds) for a countdown. It returns minutes and seconds as "m:ss". Examples: formatTime(30) is "0:30", formatTime(65) is "1:05", formatTime(0) is "0:00".

Task 2 · Code · Javascript

Write debounce(fn, wait). It returns a new function. Every call to the new function cancels the previous timer (clearTimeout) and starts a new one (setTimeout). When wait milliseconds pass with no new call, fn runs once with the latest arguments.

Task 3 · Practice

Build an "OTP login" page in your browser. It has a mobile number box, a "Send OTP" button and a "Resend OTP" button. After Send OTP, the Resend button is disabled and counts down from 30 using setInterval and your formatTime. At 0, clear the interval and enable the button. Add a search box below that logs its value with a 300 ms debounce.

Quick quiz

Question 1

1.What does setTimeout(showAd, 2000) do?

Question 2

2.How do you stop a timer started with const id = setInterval(tick, 1000)?

Question 3

3.A search box calls the server on every letter typed. What fixes this?

Question 4

4.Why might setTimeout(fn, 1000) run after 1.4 seconds?

Question 5

5.Which is best for a smooth animation written in JavaScript?

Read more (free)

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

← Previous: Forms with JavaScript