Nodejs
async
await
delay
JavaScript

Nodejs async / await with delay

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

In Node.js, the usual way to "sleep" for a short period is not to block the process. Instead, you create a promise that resolves later and await it. That gives you pause-like behavior inside an async function while leaving the event loop free to handle other work.

Build a Reusable Delay Helper

await only works with promises, so the standard pattern is to wrap setTimeout in one small helper:

javascript
1function delay(ms) {
2  return new Promise(resolve => {
3    setTimeout(resolve, ms);
4  });
5}
6
7async function run() {
8  console.log("start");
9  await delay(1000);
10  console.log("one second later");
11}
12
13run();

This is the core idea behind "async and await with delay." The function returns immediately, Node.js keeps serving other callbacks, and your async function resumes after the timer fires.

That is very different from a blocking sleep in other languages. Blocking the main thread in Node.js would freeze timers, request handlers, and all other work in the process.

Use Delay in Real Workflows

A delay helper becomes useful when you need pacing or retries. For example, a polling loop can wait between requests without making the code unreadable:

javascript
1async function pollStatus(fetchStatus) {
2  for (let attempt = 1; attempt <= 5; attempt += 1) {
3    const status = await fetchStatus();
4    console.log(`attempt ${attempt}: ${status}`);
5
6    if (status === "ready") {
7      return status;
8    }
9
10    await delay(2000);
11  }
12
13  throw new Error("Timed out waiting for ready status");
14}

The same pattern is common in retry logic after rate limiting or temporary network failures:

javascript
1async function fetchWithRetry(url, attempts = 3) {
2  for (let attempt = 1; attempt <= attempts; attempt += 1) {
3    try {
4      const response = await fetch(url);
5      if (!response.ok) {
6        throw new Error(`HTTP ${response.status}`);
7      }
8      return await response.json();
9    } catch (error) {
10      if (attempt === attempts) {
11        throw error;
12      }
13
14      await delay(500 * attempt);
15    }
16  }
17}

Because the wait happens through a promise, the code reads top to bottom while still behaving asynchronously.

Delay With Cancellation

Sometimes a delayed task should be cancelable. One approach is to connect the timer to an AbortSignal:

javascript
1function delayWithAbort(ms, signal) {
2  return new Promise((resolve, reject) => {
3    const timeoutId = setTimeout(resolve, ms);
4
5    signal.addEventListener("abort", () => {
6      clearTimeout(timeoutId);
7      reject(new Error("Delay aborted"));
8    }, { once: true });
9  });
10}

This is useful in servers where a request is canceled and there is no reason to keep waiting before the next step.

Why Not Use Busy Waiting

New Node.js developers sometimes look for a synchronous pause using a while loop and Date.now(). That is almost always the wrong move. A busy wait pins the JavaScript thread and prevents the process from handling network traffic, timers, and file system callbacks.

If you really need work to happen later, use timers. If you need CPU-heavy processing, move it to a worker thread or another service.

Common Pitfalls

The most common mistake is forgetting to return the promise from the delay helper. If delay does not return a promise, await delay(1000) finishes immediately.

Another issue is using Array.prototype.forEach with await. forEach does not wait for async callbacks, so the delays do not run in sequence. Use for...of when order matters.

Developers also confuse "waiting" with "blocking." await pauses one async function, but it does not stop the Node.js process from doing other work.

Summary

  • In Node.js, a delay is usually a promise wrapped around setTimeout.
  • 'await delay(ms) pauses the current async function without blocking the event loop.'
  • This pattern is useful for retries, polling, pacing, and backoff logic.
  • Add cancellation when delayed work may become irrelevant.
  • Avoid busy waiting, because it blocks the single JavaScript thread.

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.