Repository: toss/es-toolkit
File: src/function/retry.ts (TypeScript)

Code before:
 *
 * // Retry a function with a delay increasing linearly by 50ms per attempt
 * retry(() => fetchData(), { delay: (attempts) => attempt * 50, retries: 5 });
 *
 * @example
 * // Retry a function with exponential backoff + jitter (max delay 10 seconds)
 * retry(() => fetchData(), {
 *   delay: (attempts) => Math.min(Math.random() * 100 * 2 ** attempts, 10000),
 *   retries: 5
 * });
 */
export async function retry<T>(func: () => Promise<T>, options: RetryOptions): Promise<T>;

/**
 * Retries a function that returns a promise with specified options.
 *
 * @template T
 * @param {() => Promise<T>} func - The function to retry. It should return a promise.
 * @param {number | RetryOptions} [_options] - Either the number of retries or an options object.
 * @returns {Promise<T>} A promise that resolves with the value of the successful function call.
 */
export async function retry<T>(func: () => Promise<T>, _options?: number | RetryOptions): Promise<T> {
  let delay: number | ((attempts: number) => number);
  let retries: number;
  let signal: AbortSignal | undefined;
  let shouldRetry: ((error: unknown, attempt: number) => boolean) | undefined;

  if (typeof _options === 'number') {
    delay = DEFAULT_DELAY;
    retries = _options;
    signal = undefined;
    shouldRetry = undefined;
  } else {
    delay = _options?.delay ?? DEFAULT_DELAY;
    retries = _options?.retries ?? DEFAULT_RETRIES;
    signal = _options?.signal;
    shouldRetry = _options?.shouldRetry;
  }

  let error;

  for (let attempts = 0; attempts < retries; attempts++) {
    if (signal?.aborted) {
      throw error ?? new Error(`The retry operation was aborted due to an abort signal.`);
    }

    try {
      return await func();
    } catch (err) {
      error = err;


Review comment (suggestion):
```suggestion
  let shouldRetry: ((error: unknown, attempt: number) => boolean) = () => true;
```

Code after:
 * retry(() => fetchData(), { delay: 1000, retries: 5 });
 *
 * // Retry a function with a delay increasing linearly by 50ms per attempt
 * retry(() => fetchData(), { delay: (attempts) => attempt * 50, retries: 5 });
 *
 * @example
 * // Retry a function with exponential backoff + jitter (max delay 10 seconds)
 * retry(() => fetchData(), {
 *   delay: (attempts) => Math.min(Math.random() * 100 * 2 ** attempts, 10000),
 *   retries: 5
 * });
 */
export async function retry<T>(func: () => Promise<T>, options: RetryOptions): Promise<T>;

/**
 * Retries a function that returns a promise with specified options.
 *
 * @template T
 * @param {() => Promise<T>} func - The function to retry. It should return a promise.
 * @param {number | RetryOptions} [_options] - Either the number of retries or an options object.
 * @returns {Promise<T>} A promise that resolves with the value of the successful function call.
 */
export async function retry<T>(func: () => Promise<T>, _options?: number | RetryOptions): Promise<T> {
  let delay: number | ((attempts: number) => number);
  let retries: number;
  let signal: AbortSignal | undefined;
  let shouldRetry: (error: unknown, attempt: number) => boolean;

  if (typeof _options === 'number') {
    delay = DEFAULT_DELAY;
    retries = _options;
    signal = undefined;
    shouldRetry = DEFAULT_SHOULD_RETRY;
  } else {
    delay = _options?.delay ?? DEFAULT_DELAY;
    retries = _options?.retries ?? DEFAULT_RETRIES;
    signal = _options?.signal;
    shouldRetry = _options?.shouldRetry ?? DEFAULT_SHOULD_RETRY;
  }

  let error;

  for (let attempts = 0; attempts < retries; attempts++) {
    if (signal?.aborted) {
      throw error ?? new Error(`The retry operation was aborted due to an abort signal.`);
    }

    try {
      return await func();
    } catch (err) {
      error = err;