JavaScript
async/await
setTimeout
programming
asynchronous

async/await function does not wait for setTimeout to finish

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

await only works with Promises, but setTimeout does not return a Promise — it returns a numeric timer ID. Writing await setTimeout(fn, 1000) does not pause execution for 1 second. The await resolves immediately with the timer ID, and the callback runs later. The fix is to wrap setTimeout in a Promise so that await can properly wait for the delay to complete.

The Problem

javascript
1async function doWork() {
2  console.log("Start");
3  await setTimeout(() => {
4    console.log("Timeout done");
5  }, 2000);
6  console.log("End");
7}
8
9doWork();
10// Output:
11// Start
12// End          ← runs immediately, does not wait
13// Timeout done ← runs 2 seconds later

await expects a Promise (a thenable). setTimeout returns a number (the timer ID). When you await a non-Promise value, JavaScript wraps it in Promise.resolve(timerId), which resolves instantly.

The Fix: Wrap setTimeout in a Promise

javascript
1function delay(ms) {
2  return new Promise(resolve => setTimeout(resolve, ms));
3}
4
5async function doWork() {
6  console.log("Start");
7  await delay(2000);
8  console.log("End"); // runs after 2 seconds
9}
10
11doWork();
12// Output:
13// Start
14// (2 second pause)
15// End

The delay function creates a Promise that resolves when setTimeout fires. Now await has a real Promise to wait on.

Using Node.js Built-In timers/promises

Node.js 16+ provides a Promise-based setTimeout out of the box:

javascript
1import { setTimeout } from 'timers/promises';
2
3async function doWork() {
4  console.log("Start");
5  await setTimeout(2000); // Note: delay is the first argument, no callback
6  console.log("End");
7}
8
9doWork();
10// Start
11// (2 second pause)
12// End

This is the cleanest approach in Node.js — no need to write your own wrapper.

Running Code After the Delay

If you need to run logic after the delay:

javascript
1function delay(ms) {
2  return new Promise(resolve => setTimeout(resolve, ms));
3}
4
5async function fetchWithRetry(url, retries = 3) {
6  for (let i = 0; i < retries; i++) {
7    try {
8      const response = await fetch(url);
9      if (response.ok) return await response.json();
10    } catch (err) {
11      console.log(`Attempt ${i + 1} failed, retrying...`);
12    }
13    await delay(1000 * (i + 1)); // Wait 1s, 2s, 3s between retries
14  }
15  throw new Error(`Failed after ${retries} retries`);
16}

Cancellable Delay

Sometimes you need to cancel a pending delay:

javascript
1function cancellableDelay(ms) {
2  let timerId;
3  const promise = new Promise((resolve, reject) => {
4    timerId = setTimeout(resolve, ms);
5  });
6  return {
7    promise,
8    cancel: () => clearTimeout(timerId)
9  };
10}
11
12async function example() {
13  const { promise, cancel } = cancellableDelay(5000);
14
15  // Cancel after 1 second
16  setTimeout(() => {
17    cancel();
18    console.log("Delay cancelled");
19  }, 1000);
20
21  await promise; // This will hang since resolve is never called after cancel
22}

With AbortController (Node.js timers/promises):

javascript
1import { setTimeout } from 'timers/promises';
2
3const controller = new AbortController();
4
5// Cancel after 1 second
6setTimeout(1000).then(() => controller.abort());
7
8try {
9  await setTimeout(5000, null, { signal: controller.signal });
10} catch (err) {
11  if (err.code === 'ABORT_ERR') {
12    console.log("Timer was cancelled");
13  }
14}

Why setInterval Has the Same Issue

javascript
1// This does NOT work — same reason as setTimeout
2async function poll() {
3  await setInterval(() => {
4    console.log("tick");
5  }, 1000);
6  console.log("done"); // runs immediately
7}
8
9// Use a loop with delay instead
10async function poll() {
11  for (let i = 0; i < 5; i++) {
12    console.log("tick");
13    await new Promise(resolve => setTimeout(resolve, 1000));
14  }
15  console.log("done");
16}

Common Pitfalls

  • Assuming await pauses any asynchronous function: await only pauses on Promises. Callback-based APIs like setTimeout, setInterval, fs.readFile (callback version), and event listeners do not return Promises, so await has no effect on them.
  • Wrapping the callback, not the delay: Writing await new Promise(resolve => setTimeout(() => { doStuff(); resolve(); }, 1000)) works but is verbose. Separate the delay from the logic — await delay(1000); doStuff(); is cleaner.
  • Forgetting that setTimeout(fn, 0) is not instant: setTimeout(fn, 0) still defers fn to the next iteration of the event loop. Wrapping it in a Promise and awaiting it introduces a microtask + macrotask boundary, which can affect ordering with other Promises.
  • Using await in a forEach callback: Array.forEach ignores the return value (and thus the Promise) from async callbacks. Use for...of or Promise.all(arr.map(...)) instead for sequential or parallel async operations.
  • Not handling errors in delayed operations: If the code after await delay(ms) throws, the error propagates as an unhandled Promise rejection unless the caller uses try/catch or .catch().

Summary

  • setTimeout returns a timer ID, not a Promise — await does not wait for it
  • Wrap setTimeout in a Promise: new Promise(resolve => setTimeout(resolve, ms))
  • In Node.js 16+, use import { setTimeout } from 'timers/promises' for a built-in Promise-based delay
  • Use AbortController with timers/promises for cancellable delays
  • Replace setInterval with for loops and await delay() for async-friendly polling
  • Only await values that are Promises — callback APIs require wrapping first

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.