Asynchronous JavaScript

Discover JavaScript Timers

Learn how setTimeout and setInterval schedule JavaScript tasks, cancel timers, handle timing delays, and avoid overlapping asynchronous work.

JavaScript timers schedule a function to run later. Use setTimeout() once. Use setInterval() for repeated runs.

The delay is a minimum. Busy main-thread work, background tabs, and browser throttling can push callbacks later than you expect.

Run code later with setTimeout

setTimeout(() => {
  console.log('Two seconds passed')
}, 2000)

setTimeout() returns an ID. Save it if you might cancel:

const timerId = setTimeout(() => {
  console.log('Saved')
}, 2000)

clearTimeout(timerId)

clearTimeout() after the callback ran does nothing.

Pass arguments after the delay:

function greet(name) {
  console.log(`Hello ${name}`)
}

setTimeout(greet, 1000, 'Flavio')

I often wrap that in an arrow function for clarity:

setTimeout(() => greet('Flavio'), 1000)

Always pass a function. A string argument runs through eval() and is unsafe.

What a zero delay means

0 does not mean immediate:

setTimeout(() => {
  console.log('timer')
}, 0)

console.log('current task')

Output:

current task
timer

The current stack and microtasks finish first. Zero delay only queues a macrotask.

Repeat code with setInterval

const intervalId = setInterval(() => {
  console.log(new Date().toLocaleTimeString())
}, 1000)

Stop with clearInterval(intervalId).

setInterval does not wait for async work inside the callback to finish:

setInterval(async () => {
  await fetch('/api/status')
}, 1000)

Overlapping requests can pile up and finish out of order.

Wait for work before scheduling again

For polling, chain setTimeout after each await:

let stopped = false

async function poll() {
  try {
    const response = await fetch('/api/status')
    const status = await response.json()
    console.log(status)
  } finally {
    if (!stopped) {
      setTimeout(poll, 1000)
    }
  }
}

poll()

The next delay starts after the request settles.

Timers are not precise clocks

Late timers happen when JavaScript is busy, the tab is hidden, or the device saves power. Use performance.now() when you need elapsed time. Use requestAnimationFrame() for visual animation tied to rendering.

Browsers and Node.js both expose timers, but return types and extra APIs like setImmediate() differ on Node.

See MDN for setTimeout() and setInterval().

Run the zero-delay snippet above in the console. Seeing current task first is the whole lesson in one output.

If a timer feels late, log Date.now() inside the callback and compare it to when you scheduled the work.

Lesson completed