Implement promiseTimeout() — Race a Promise Against a Deadline

promiseTimeout(promise, ms) rejects with a timeout error if the promise doesn't settle within the given time. This is a direct application of Promise.race with a timer.

8 min read
JavaScript
Interview
Implementation
Promises

TABLE OF CONTENTS

promiseTimeout(promise, ms) rejects with a timeout error if the promise doesn't settle within the given time. It's a direct application of Promise.race against a timer — but the interviewer also wants to see cleanup and a higher-order version.


What is promiseTimeout()?

promiseTimeout(promise, ms) races a promise against a timer. If the input promise settles within ms milliseconds, its result is used. If it doesn't, the returned promise rejects with a timeout error. It's a deadline enforcement pattern — "get me this result, but don't make me wait forever."

The mechanism is straightforward: create a timer promise that rejects after ms, then race it against the input. Promise.race is the natural fit — the first to settle wins. But there's a cleanup concern: if the input promise settles first, the timer keeps running (a dangling setTimeout). The thorough implementation clears the timer on early settlement.

Real-world use cases:

  • Fetch with timeout — the native fetch API has no built-in timeout. promiseTimeout(fetch(url), 5000) adds one
  • API gateways — enforce SLAs by rejecting requests that take longer than N seconds
  • User-facing loading states — show a "taking longer than expected" message after 3 seconds, even if the request eventually succeeds
  • Health checkspromiseTimeout(ping(service), 2000) ensures liveness probes fail fast

The interviewer will often ask for the higher-order version: withTimeout(fn, ms) that wraps any async function so every call is automatically time-limited. This tests whether you can generalize from a one-shot timeout to a reusable decorator.


The Problem

"Implement promiseTimeout(promise, ms) that returns a promise. If promise settles within ms milliseconds, return its result. If it doesn't, reject with a timeout error."

"Bonus: make it a higher-order function withTimeout(fn, ms) that wraps any async function."


Thought Process

You're racing two things:

  1. The original promise
  2. A timer that rejects after ms

Promise.race is the natural tool — first to settle wins. But you need to clean up the timer if the original promise settles first, to avoid a hanging timeout.

The pattern:

  • Create a timer promise that rejects after ms
  • Race it against the input promise
  • When the race settles, clear the timer
  • Optionally wrap with Promise.race or a custom race that handles cleanup

Step 1 — Base Implementation

Loading editor...


Step 2 — Cleaning Up the Timer

The interviewer asks: "What about the timer when the promise resolves? You're leaking a setTimeout."

Loading editor...


Step 3 — Custom Error Class

Loading editor...


Step 4 — Higher-Order withTimeout

Loading editor...


Step 5 — Edge Cases

Already-settled promise: Promise.race resolves immediately with the already-settled promise's value. The timer never fires — but we still clean up with .finally().

Timeout = 0: setTimeout(fn, 0) fires on the next macrotask. If the promise is already resolved (microtask), the race resolves first. If the promise is genuinely async, the timeout wins — correct behavior.

Promise rejects before timeout: Still works — Promise.race forwards the rejection. The timer is cleaned up by .finally().


Full Solution

Loading editor...


What Interviewers Are Testing

  • Promise.race understanding — knowing that race settles with the first to settle
  • Timer cleanup — using clearTimeout to prevent leaks
  • Higher-order function pattern — wrapping a function with timeout behavior
  • Custom error types — creating distinguishable error classes

Complexity

TimeSpace
promiseTimeoutO(1)O(1)

Interview Tips

  • Start with Promise.race — "This is a race between the promise and a timeout." This tells the interviewer you recognize the pattern.
  • Add timer cleanup without being asked — it shows you think about resource management.
  • Create a custom errorinstanceof TimeoutError lets callers distinguish timeout from other rejections.
  • Show withTimeout as a natural extension — "Once you have promiseTimeout, wrapping any async function is trivial. Here's the HOF version."

Related Questions


Let's Connect

© 2026 Naveen Karthik // Built with React & MUI