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.
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
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
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:
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:
Cancellable Delay
Sometimes you need to cancel a pending delay:
With AbortController (Node.js timers/promises):
Why setInterval Has the Same Issue
Common Pitfalls
- Assuming
awaitpauses any asynchronous function:awaitonly pauses on Promises. Callback-based APIs likesetTimeout,setInterval,fs.readFile(callback version), and event listeners do not return Promises, soawaithas 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 defersfnto 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
awaitin aforEachcallback:Array.forEachignores the return value (and thus the Promise) fromasynccallbacks. Usefor...oforPromise.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 usestry/catchor.catch().
Summary
setTimeoutreturns a timer ID, not a Promise —awaitdoes not wait for it- Wrap
setTimeoutin 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
AbortControllerwithtimers/promisesfor cancellable delays - Replace
setIntervalwithforloops andawait delay()for async-friendly polling - Only
awaitvalues that are Promises — callback APIs require wrapping first
Related reading
- Async/Await implementation of WebBrowser class for .NET
- Async/Await not running in Azure App Service
- Async/await, ThreadPool and SetApartmentState
- Async/Await vs Threads
- Asynchronous async await method to generate pdf
- Asynchronous Bounded Queue in JS/TS using async/await
- Async/Await vs Threads
- async/await. Where is continuation of awaitable part of method performed?
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack 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.