Sleep with a Promise
Pausing async code is a one-line problem until you need cancellation, a typed signal, or to reuse the helper across files. This snippet starts with the canonical `setTimeout` Promise wrapper, then adds `AbortSignal` support so callers can cancel waits cleanly. Drop it into any toolkit and stop reaching for `setTimeout` callbacks in async functions.
239 views
5
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function demo() {
console.log('start');
await sleep(50);
console.log('done after 50ms');
}
demo();The canonical sleep wraps setTimeout in a Promise so it composes inside async functions with await. The resolver is the timeout callback, so the promise settles exactly once after ms milliseconds. Use it for retry backoffs, demo pauses, or rate-limited loops where you do not need to cancel the wait. The function is O(1) time and uses one timer slot per pending sleep, so do not spawn thousands of them in a tight loop without cleaning up.
function sleep(ms, signal) {
return new Promise((resolve, reject) => {
if (signal && signal.aborted) {
return reject(new DOMException('Aborted', 'AbortError'));
}
const id = setTimeout(() => {
if (signal) signal.removeEventListener('abort', onAbort);
resolve();
}, ms);
function onAbort() {
clearTimeout(id);
reject(new DOMException('Aborted', 'AbortError'));
}
if (signal) signal.addEventListener('abort', onAbort, { once: true });
});
}
async function demo() {
const ac = new AbortController();
setTimeout(() => ac.abort(), 10);
try {
await sleep(1000, ac.signal);
} catch (err) {
console.log('cancelled:', err.name);
}
}
demo();Adding AbortSignal support turns sleep into a first-class cancellable primitive that fits the modern Web API contract. The wrapper checks for an already-aborted signal up front, registers an abort listener that clears the timer and rejects with an AbortError, and tears the listener down on natural completion to avoid leaks. Callers can chain it with fetch, axios cancel tokens, or any other abort-aware helper using a shared AbortController. This is the pattern most utility libraries (p-timeout, delay) settled on for compatibility.
