$ snippet sleep-timeout-abort

Sleep, timeout and cancel with AbortController

An awaitable sleep, a withTimeout() wrapper for any promise, and cancellation that actually stops the underlying fetch.

JavaScript
/** await sleep(500) — cancellable via an AbortSignal. */
function sleep(ms, signal) {
  return new Promise((resolve, reject) => {
    if (signal?.aborted) return reject(signal.reason);
    const t = setTimeout(resolve, ms);
    signal?.addEventListener("abort", () => { clearTimeout(t); reject(signal.reason); }, { once: true });
  });
}

/** Reject if `promise` takes longer than `ms`. Use with fetch(url, { signal }) to also cancel the request. */
function withTimeout(promise, ms, message = `Timed out after ${ms} ms`) {
  let t;
  const timeout = new Promise((_, reject) => { t = setTimeout(() => reject(new Error(message)), ms); });
  return Promise.race([promise, timeout]).finally(() => clearTimeout(t));
}

/** Fetch that gives up (and closes the connection) after `ms`. */
async function fetchWithTimeout(url, options = {}, ms = 10000) {
  const controller = new AbortController();
  const t = setTimeout(() => controller.abort(new Error(`Request timed out after ${ms} ms`)), ms);
  try {
    return await fetch(url, { ...options, signal: controller.signal });
  } finally {
    clearTimeout(t);
  }
}

How it works

Promise.race alone gives you a timeout that reports — but the slow operation keeps running. To actually cancel network work you need an AbortSignal, which fetch understands. Modern engines also ship AbortSignal.timeout(ms), which does the controller-plus-setTimeout dance for you: fetch(url, { signal: AbortSignal.timeout(5000) }).

Gotchas

Try it
Result
Ad slot