All files / src/utils/async with-timeout.ts

88.88% Statements 8/9
50% Branches 1/2
100% Functions 4/4
87.5% Lines 7/8

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 53 54 55 56 57                                                                          29x         9x 9x 5x   4x 4x             9x    
import { CanceledError } from '../common/canceled-error.ts';
import { cancelableDelay } from './delay.ts';
 
/**
 * Wraps a given promise and resolves with its value if it completes within the specified timeout. If the timeout
 * elapses, the returned promise resolves with a provided fallback value or undefined if no `timeoutValue` is provided.
 *
 * Note: The original promise continues to run in the background even if the timeout occurs. If caching promises,
 * consider caching the original promise instead of the timed one, since it may resolve successfully on a subsequent
 * access. If the original promise is not consumed elsewhere and later rejects, it may trigger an unhandled rejection;
 * consider attaching a `.catch()` to the original or otherwise observing its outcome.
 *
 * @example
 *
 * ```ts
 * import { withTimeout } from 'emitnlog/utils';
 *
 * const promise: Promise<string | undefined> = withTimeout(fetchContent(uri), 5000);
 * const content: string = await promise.then((value) => value ?? '');
 * ```
 *
 * @example
 *
 * ```ts
 * import { withTimeout } from 'emitnlog/utils';
 *
 * const promise: Promise<string | -1> = withTimeout(fetchContent(uri), 5000, -1);
 * const content: string | undefined = await promise.then((value) => (value === -1 ? undefined : value));
 * ```
 *
 * @param promise The promise to be wrapped with a timeout.
 * @param timeout The maximum duration (in milliseconds) to wait before resolving with `timeoutValue`. (0 if negatived,
 *   and ceil if decimal).
 * @param timeoutValue The value to resolve with if the timeout is reached before the promise completes.
 * @returns A promise that resolves with the original promise's value if completed within the timeout, or with
 *   `timeoutValue` otherwise.
 */
export const withTimeout = <T, const R = undefined>(
  promise: Promise<T>,
  timeout: number,
  timeoutValue?: R,
): Promise<T | R> => {
  const { promise: delayPromise, cancel } = cancelableDelay(timeout);
  const timeoutPromise = delayPromise.then(
    () => timeoutValue as R,
    (error: unknown) => {
      Eif (error instanceof CanceledError) {
        return new Promise<R>(() => void 0);
      }
 
      throw error;
    },
  );
 
  return Promise.race<T | R>([promise.finally(cancel), timeoutPromise]);
};