All files / promise/src anySerial.ts

100% Statements 10/10
100% Branches 2/2
100% Functions 2/2
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 531x                                                     4x     1x           4x 3x 3x   2x         3x 2x       1x    
import { isEmpty } from "@benneq/array";
 
/**
 * Executes all {@link Promise}s in serial order and returns either the first
 * result or throws an {@link AggregateError} containing all errors.
 *
 * An optional `defaultValue` can be provided that will be returned if
 * `promises` is empty.
 *
 * If no {@link Promise}s and no `defaultValue` were provided `undefined` will
 * be returned.
 *
 * @example
 * Execute all {@link Promise}s in serial
 * ```ts
 * const promise = anySerial([
 *   Promise.reject("err"),
 *   Promise.resolve(1)
 * ]);
 *
 * promise.then(v => console.log(v)); // 1
 * ```
 *
 * @param promises - the {@link Promise}s to execute in serial order
 * @param defaultValue - the default value to return if no promise was provided
 * @returns a {@link Promise} that executes all {@link Promise}s in serial
 */
export const anySerial: {
  <T>(promises: Iterable<Promise<T>>): Promise<T | undefined>;
  <T>(promises: Iterable<Promise<T>>, defaultValue: T): Promise<T>;
} = async <T>(
  promises: Iterable<Promise<T>>,
  defaultValue?: T,
  // internal variables:
  errors: unknown[] = []
): Promise<T | undefined> => {
  for (const promise of promises) {
    try {
      return await promise;
    } catch (err) {
      errors.push(err);
    }
  }
 
  // if no promise was provided
  if (isEmpty(errors)) {
    return defaultValue;
  }
 
  // if all promises rejected
  throw new AggregateError(errors);
};