const STATE_PENDING = 'pending'; type NotReadonly = {-readonly [P in keyof T]: T[P]}; type PromiseExecuter = ( resolve: (value: Awaited) => void, reject: (reason: unknown) => void, controller: AbortController, ) => T /** OpenPromise Object */ export type OpenPromise = readonly [ promise: Promise, resolve: (value: T | Promise) => void, reject: (reason: unknown) => void, controller: AbortController, ] & { readonly state: 'pending' | 'fulfilled' | 'rejected'; readonly promise: Promise; readonly resolve: (value: T | Promise) => void; readonly reject: (reason: unknown) => void; readonly controller: AbortController; }; /** Create open promise with optional executer */ export function createOpenPromise(executer?: PromiseExecuter): OpenPromise>; /** Create open promise with AbortController or AbortSignal and optional executer */ export function createOpenPromise( ctrlOrSignal: AbortController | AbortSignal, executer?: PromiseExecuter, ): OpenPromise>; export function createOpenPromise( executerOrCtrlOrSignal?: PromiseExecuter | AbortController | AbortSignal, executer?: PromiseExecuter, ): OpenPromise> { // Create OpenTuple const open = Array(4) as unknown as NotReadonly>>; // Initial State open.state = STATE_PENDING; // Lazy AbortController (to not care about of polyfill) let ctrl: AbortController | undefined; let ctrlInited = false const initCtrl = () => { ctrl ||= new AbortController(); if (!ctrlInited) { ctrlInited = true; ctrl.signal.addEventListener('abort', (e) => { open.reject(ctrl!.signal.reason); }); } return ctrl; } // AbortController or AbortSignal passed if (executerOrCtrlOrSignal instanceof AbortController) { ctrl = executerOrCtrlOrSignal; initCtrl(); } else if (executerOrCtrlOrSignal instanceof AbortSignal) { ctrl = new AbortController(); if (executerOrCtrlOrSignal.aborted) { ctrl!.abort(executerOrCtrlOrSignal.reason); } else { executerOrCtrlOrSignal.addEventListener('abort', () => { ctrl!.abort(executerOrCtrlOrSignal.reason); }); } initCtrl(); } else { executer = executerOrCtrlOrSignal as PromiseExecuter; } // Add a lazy getter to get AbortController const ctrlPropDescr = {get: initCtrl}; Object.defineProperties(open, { 3: ctrlPropDescr, // by Index controller: ctrlPropDescr, }); // Create promise open[0] = open.promise = new Promise((originalResolve, originalReject) => { // Resolve const resolve = open[1] = open.resolve = (value) => { if (open.state !== STATE_PENDING) return; open.state = 'fulfilled'; originalResolve(value); }; // Reject const reject = open[2] = open.reject = (reason) => { if (open.state !== STATE_PENDING) return; open.state = 'rejected'; originalReject(reason); ctrl?.abort(reason); }; // Check AbortController if (ctrl?.signal.aborted) { reject(ctrl.signal.reason); return; } // Run executer? if (executer) { Promise.resolve().then(() => { if (open.state !== STATE_PENDING) return; try { const result = executer( resolve, reject, executer.length > 2 ? initCtrl() : null as any, ) as Awaited; if (result && typeof result === 'object' && 'then' in result) { resolve(result); } else if (executer.length === 0 || result !== undefined) { resolve(result); } } catch (err) { reject(err); } }); } }); return open; };