Files
my-pi/pi-subagents/src/abortable.ts
T

44 lines
1.4 KiB
TypeScript

/**
* abortable.ts — race a promise against an AbortSignal without cancelling the
* underlying work.
*
* Used by the `get_subagent_result` wait paths (top-level and nested): pressing
* Esc cancels only the caller's wait; the background child keeps running and its
* result stays unconsumed. The listener is removed on every settle path so the
* signal accumulates no handlers, and a late settlement of the wrapped promise
* after an abort is absorbed as a no-op (no unhandled rejection).
*/
/** Await a promise until it settles or the caller cancels, without aborting the underlying work. */
export function abortable<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {
if (!signal) return promise;
if (signal.aborted) return Promise.reject(signal.reason);
return new Promise<T>((resolve, reject) => {
let settled = false;
const cleanup = () => signal.removeEventListener("abort", onAbort);
const onAbort = () => {
if (settled) return;
settled = true;
cleanup();
reject(signal.reason);
};
signal.addEventListener("abort", onAbort, { once: true });
promise.then(
(value) => {
if (settled) return;
settled = true;
cleanup();
resolve(value);
},
(error: unknown) => {
if (settled) return;
settled = true;
cleanup();
reject(error);
},
);
});
}