Skip to content

Instantly share code, notes, and snippets.

@bakerface
Created April 3, 2025 11:34
Show Gist options
  • Select an option

  • Save bakerface/f5402281f265ce751c7751a7120cd2aa to your computer and use it in GitHub Desktop.

Select an option

Save bakerface/f5402281f265ce751c7751a7120cd2aa to your computer and use it in GitHub Desktop.
Abortable promises in TypeScript
import { withAbortableResolvers } from "./with-abortable-resolvers";
export function sleep(ms: number, signal?: AbortSignal): Promise<void> {
const { promise, resolve } = withAbortableResolvers<void>(signal);
const handle = setTimeout(resolve, ms);
return promise.finally(() => clearTimeout(handle));
}
export function withAbortableResolvers<T>(signal?: AbortSignal): PromiseWithResolvers<T> {
const res = withResolvers<T>();
if (!signal) {
return res;
}
if (signal.aborted) {
res.reject(signal.reason);
return res;
}
const abort = () => res.reject(signal.reason);
signal.addEventListener("abort", abort);
res.promise = res.promise.finally(() => {
signal.removeEventListener("abort", abort);
});
return res;
}
// NOTE:
// everything below here can be deleted once Promise.withResolvers is widely available
function withResolvers<T>(): PromiseWithResolvers<T> {
let resolve: ResolvePromise<T> = throwPromiseExecutorError;
let reject: RejectPromise = throwPromiseExecutorError;
const promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
function throwPromiseExecutorError(): never {
throw new Error("The promise executor was deferred.");
}
export type ResolvePromise<T> = (value: T | PromiseLike<T>) => void;
export type RejectPromise = (reason: Error) => void;
export interface PromiseWithResolvers<T> {
promise: Promise<T>;
resolve: ResolvePromise<T>;
reject: RejectPromise;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment