Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
49 lines
1.3 KiB
TypeScript
49 lines
1.3 KiB
TypeScript
function abortReason(signal: AbortSignal): unknown {
|
|
if (signal.reason !== undefined) return signal.reason;
|
|
const error = new Error("The operation was aborted");
|
|
error.name = "AbortError";
|
|
return error;
|
|
}
|
|
|
|
/** Normalize an optional public signal without imposing a deadline. */
|
|
export function operationSignal(signal?: AbortSignal): AbortSignal {
|
|
return signal ?? new AbortController().signal;
|
|
}
|
|
|
|
/** Stop waiting on abort while observing the abandoned operation through settlement. */
|
|
export function raceWithAbortSignal<T>(operation: Promise<T>, signal: AbortSignal | undefined): Promise<T> {
|
|
if (!signal) return operation;
|
|
if (signal.aborted) {
|
|
void operation.catch(() => {});
|
|
return Promise.reject(abortReason(signal));
|
|
}
|
|
|
|
return new Promise<T>((resolve, reject) => {
|
|
let settled = false;
|
|
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
const onAbort = () => {
|
|
if (settled) return;
|
|
settled = true;
|
|
cleanup();
|
|
reject(abortReason(signal));
|
|
};
|
|
|
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
void operation.then(
|
|
(value) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
cleanup();
|
|
resolve(value);
|
|
},
|
|
(error: unknown) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
cleanup();
|
|
reject(error);
|
|
},
|
|
);
|
|
if (signal.aborted) onAbort();
|
|
});
|
|
}
|