import {AbortError, isAbortError} from './AbortError'; /** * Abortable version of `Promise.all`. * * Creates new inner `AbortSignal` and passes it to `executor`. That signal is * aborted when `signal` is aborted or any of the promises returned from * `executor` are rejected. * * Returns a promise that fulfills with an array of results when all of the * promises returned from `executor` fulfill, rejects when any of the * promises returned from `executor` are rejected, and rejects with `AbortError` * when `signal` is aborted. * * The promises returned from `executor` must be abortable, i.e. once * `innerSignal` is aborted, they must reject with `AbortError` either * immediately, or after doing any async cleanup. * * Example: * * const [result1, result2] = await all(signal, signal => [ * makeRequest(signal, params1), * makeRequest(signal, params2), * ]); */ export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [ PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, ], ): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [ PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, ], ): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [ PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, ], ): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [ PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, ], ): Promise<[T1, T2, T3, T4, T5, T6, T7]>; export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [ PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, ], ): Promise<[T1, T2, T3, T4, T5, T6]>; export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [ PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike, ], ): Promise<[T1, T2, T3, T4, T5]>; export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [ PromiseLike, PromiseLike, PromiseLike, PromiseLike, ], ): Promise<[T1, T2, T3, T4]>; export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [PromiseLike, PromiseLike, PromiseLike], ): Promise<[T1, T2, T3]>; export function all( signal: AbortSignal, executor: ( innerSignal: AbortSignal, ) => readonly [PromiseLike, PromiseLike], ): Promise<[T1, T2]>; export function all( signal: AbortSignal, executor: (innerSignal: AbortSignal) => readonly PromiseLike[], ): Promise; export function all( signal: AbortSignal, executor: (innerSignal: AbortSignal) => readonly PromiseLike[], ): Promise { return new Promise((resolve, reject) => { if (signal.aborted) { reject(signal.reason ?? new AbortError()); return; } const innerAbortController = new AbortController(); const promises = executor(innerAbortController.signal); if (promises.length === 0) { resolve([]); return; } const abortListener = () => { innerAbortController.abort(signal.reason ?? new AbortError()); }; signal.addEventListener('abort', abortListener); let rejection: {reason: any} | undefined; const results = new Array(promises.length); let settledCount = 0; function settled() { settledCount += 1; if (settledCount === promises.length) { signal.removeEventListener('abort', abortListener); if (rejection != null) { reject(rejection.reason); } else { resolve(results); } } } for (const [i, promise] of promises.entries()) { promise.then( value => { results[i] = value; settled(); }, reason => { innerAbortController.abort( new AbortError( 'One of the promises passed to all() rejected', false, ), ); if ( rejection == null || (!isAbortError(reason) && isAbortError(rejection.reason)) ) { rejection = {reason}; } settled(); }, ); } }); }