Cancelling the JavaScript sleep function
A typical JavaScript sleep implementation is this one-liner:
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
In some cases you’ll need cancel this timeout mid-flight. One elegant solution is to accept an AbortSignal similar to other async APIs like fetch.
function sleep(ms, signal) {
return new Promise((resolve, reject) => {
const onAbort = () => {
clearTimeout(timeoutId)
reject(new Error("Aborted"))
}
const timeoutId = setTimeout(() => {
signal?.removeEventListener("abort", onAbort)
resolve()
}, ms)
signal?.addEventListener("abort", onAbort)
})
}
TypeScript version
function sleep(ms: number, signal?: AbortSignal) {
return new Promise<void>((resolve, reject) => {
const onAbort = () => {
clearTimeout(timeoutId)
reject(new Error("Aborted"))
}
const timeoutId = setTimeout(() => {
signal?.removeEventListener("abort", onAbort)
resolve()
}, ms)
signal?.addEventListener("abort", onAbort)
})
}
Now you can cancel the sleep timeout with AbortController.abort().
const controller = new AbortController()
sleep(1000, controller.signal)
.then(() => console.log("done"))
.catch(() => console.log("aborted"))
// in some other handler...
controller.abort()
You could certainly return your own cancel()
method from the sleep function
along with the promise, but that would change the familiar function signature.
What I like about this abort signal approach is:
- It’s unobtrusive—the signal argument is optional, and output is still a plain promise
- You can use the same signal to cancel other async processes