export default async function doAbortable( signal: AbortSignal | undefined, func: (abortSignal: AbortSignal) => R | Promise, ): Promise { const controller = new AbortController(); const abortInner = () => controller.abort(); if (signal?.aborted) { abortInner(); } else { signal?.addEventListener("abort", abortInner, { once: true }); } try { return await func(controller.signal); } finally { // remove listener from the original signal, this allows our local controller // to be collected as there are no longer any references to it, by consequence // this allows the inner signal to be collectable as long as no references remain signal?.removeEventListener("abort", abortInner); } }