npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ts-retry

v7.0.0

Published

A little retry tool to execute a function until the function is successful. Can also bind a timeout to a function. This lib is usable in typescript, in javascript, in node, in SPA tools (rest, Vue, Svelte...) and browser (available in ESM and common js fo

Readme

ts-retry

A little retry tool to execute a function until the function is successful. Can also bind a timeout to a function. This lib is usable in typescript, in javascript, in node, in SPA tools (rest, Vue, Svelte...) and browser (available in ESM and common js format).


Recent Changes (since v5.0.0+):

  • v6.0.0: onError callback can now return a boolean to control retry behavior:
    • Returning true or nothing: continue retries (default behavior)
    • Returning false: stop retries and throw an AbortError
  • AbortError: New error type thrown when retries are aborted via onError returning false. Includes methods getError() (last error) and getCurrentTry() (attempt number).
  • v5.0.0: onError and onSuccess callbacks now receive attempt data (currentTry) for better logging.
  • v5.0.1: Fixed missing exports for retryDecorator and retryAsyncDecorator.

Breaking change: To migrate to 3.x: retryAsyncDecorator and retryAsync has been moved to utils/decorators. This impacts only those that import those functions directly from decorator.ts file. Other 3.X items are new and imply no breaking change.

For those who are using 1.x in typescript, you may have to add a type to RetryOptions if you want to use the new until function. This type is the called function's return type.

How to

  • to retry something:

    const result = await retry(
      () => {
        /* do something */
      },
      { delay: 100, maxTry: 5 },
    );
  • to retry something async :

    const result = await retryAsync(
      async () => {
        /* do something */
      },
      { delay: 100, maxTry: 5 },
    );
  • to retry until the answer is 42 :

    try {
      await retryAsync(
        async () => {
          /* do something */
        },
        {
          delay: 100,
          maxTry: 5,
          until: (lastResult) => lastResult === 42,
        },
      );
    } catch (err) {
      if (isTooManyTries(err)) {
        // Did not get 42 after 'maxTry' calls
      } else {
        // something else goes wrong
      }
    }
  • to retry with custom error handling and potential abort:

    try {
      await retryAsync(
        async () => {
          /* do something that might fail */
        },
        {
          delay: 100,
          maxTry: 5,
          onError: (err, currentTry) => {
            console.log(`Attempt ${currentTry} failed: ${err.message}`);
            // Abort if it's a specific error type
            return err.message.includes("fatal") ? false : true;
          },
        },
      );
    } catch (err) {
      if (isAbortError(err)) {
        // Retries were aborted
        console.log(`Aborted at attempt ${err.getCurrentTry()}`);
      } else if (isTooManyTries(err)) {
        // Max tries reached
      } else {
        // Other error
      }
    }
  • Need to call a function at multiple locations with the same retryOptions? Use decorators:

    const fn = (param1: string, param2: number) => /* do something */;
    const decoratedFn = retryDecorator(
      fn,
      { delay:100, maxTry:5 }
    );
    const result1 = await decoratedFn("value1", 1);
    const result2 = await decoratedFn("valueXXX", 2);
    
    const fn = async (name: string): Promise<any> => { /* something async */ };
    const decoratedFn = retryAsyncDecorator(
      fn,
      { delay:100, maxTry:5 }
    );
    const result1 = await decoratedFn("Smith");
    const result2 = await decoratedFn("Doe");
  • to wait:

    await wait(10000); // Wait for 10 seconds
  • to set a timeout:

    try {
      const result = await waitUntil(() => {
        /* do something */
      }, 10000);
    } catch (err) {
      if (isTimeoutError(err)) {
        // fn does not complete after 10 seconds
      } else {
        // the function throws an exception
      }
    }
  • to set a timeout on something async:

    try {
      const result = await waitUntilAsync(async () => {
        /* do something */
      }, 10000);
    } catch (err) {
      if (isTimeoutError(err)) {
        // fn does not complete after 10 seconds
      } else {
        // fn throws an exception
      }
    }
  • Need to call a function at multiple locations with the same retryOptions? Use decorators:

      const fn = (title: string, count: number) => /* a long task */;
      const decoratedFn = waitUntilDecorator(
        fn,
        { delay:100, maxTry:5 }
      );
      const result1 = await decoratedFn("Intro", 1);
      const result2 = await decoratedFn("A chapter", 2);
    const fn = async (name: string): Promise<any> => {
      /* a long task */
    };
    const decoratedFn = waitUntilAsyncDecorator(fn, { delay: 100, maxTry: 5 });
    const result1 = await decoratedFn("John");
    const result2 = await decoratedFn("Doe");

Utils

retry comes with handy utility functions for common use cases:

  • to retry until a function returns something defined (i.e., not null nor undefined):
  // in all cases the result is a string and cannot be null or undefined
  const result = await retryUntilDefined( (): string|undefined => { ... } );

  const result = await retryUntilAsyncDefined( (): Promise<string|null> => { ... } );

  const decorated = retryUntilDefinedDecorator( (p1: string): string|undefined => { ... } );
  const result = await decorated('hello world');

  const decorated = retryAsyncUntilDefinedDecorator( (p1: string): Promise<string|undefined> => { ... } );
  const result = await decorated('hello world');
  • to retry until a function returns something truthy:
  // in all cases the result is a string and cannot be null or undefined
  const result = await retryUntilTruthy( (): boolean|undefined => { ... } );

  const result = await retryAsyncUntilTruthy( (): Promise<number|null> => { ... } );

  const decorated = retryUntilTruthyDecorator( (p1: string): boolean|undefined => { ... } );
  const result = await decorated('hello world');

  const decorated = retryAsyncUntilTruthyDecorator( (p1: string): Promise<boolean|null> => { ... } );
  const result = await decorated('hello world');
  • to retry until fetch is successful:
  const result = await retryAsyncUntilResponse( () => fetch(...) );

  const decorated = retryAsyncUntilResponseDecorator( (param) => fetch(...) );
  const result = await decorated('q=1');

API

Retry family

  • retry(fn, retryOptions?): call repeatedly fn until fn does not throw an exception. Stop after the retryOptions.maxTry count. Between each call wait retryOptions.delay milliseconds. If we stop calling fn after retryOptions.maxTry, throw the fn exception, otherwise return the fn return value.
  • retryAsync(fn, retryOptions?): same as retry, except fn is an asynchronous function.
  • retryOptions:
    • maxTry: [optional] maximum calls to fn.
    • delay: [optional] delay between each call (in milliseconds). Could be either a number or a function (when delay time depends on the number of retries, or previous result...), see below for explanation about delay
    • until: [optional] (lastResult) => boolean: return false if the last fn result is not the expected one: continue calling fn until until returns true. A TooManyTries is thrown after maxTry calls to fn;
    • onError: [optional](err: Error, currentTry: number) => boolean | void: called on each error except the last one. Includes the current try for logging. Return true or nothing to continue retries, false to abort and throw AbortError. To catch/log the last error, use onMaxRetryFunc
  • onMaxRetryFunc: [optional](err: Error) => void: called on the final error at the maxTry limit only
  • onSuccess: [optional](currentTry: number) => void: called on success. Includes the current try for logging When an option value is not provided, the default one is applied. The default options are:
  delay: 250,
  maxTry: 4 * 60,
  • setDefaultRetryOptions<T>(retryOptions: RetryOptions<T>): change the default retryOptions.
  • getDefaultRetryOptions<T>(): returns the current default retry options.
  • retryAsyncDecorator<T>(fn: T, retryOptions?: RetryOptions<T>) and retryDecorator<T>(fn: T, retryOptions?: RetryOptions<T>): decorators that return a function with the same signature as the given function. On decorated call, fn is called repeatedly until it does not throw an exception or until retryOptions.maxTry.
  • TooManyTries: an error thrown by retry functions when until returns false after maxTry calls. It comes with a type guard and includes the last failed result:
if (isTooManyTries(error)) {
  // retry failed
  console.error(`last error is ${error.getLastResult()}`);
}
  • AbortError: an error thrown when retries are aborted via onError returning false. It comes with methods to access the last error and attempt number:
if (isAbortError(error)) {
  // retry aborted
  console.error(
    `Aborted at attempt ${error.getCurrentTry()}, last error: ${error.getError()}`,
  );
}

When delay can vary

When delay option is a function, it is called before each retry: this allows having a delay that can change between retries (e.g., delay can increase exponentially). The function receives the following parameters:

(parameter: {
  currentTry: number,
  maxTry: number,
  lastDelay?: number
  lastResult?: RETURN_TYPE,
  lastError?: Error
}) => number;

where:

  • currentTry: the number of calls to fn (first is 1, not 0).
  • maxTry: maximum calls to fn.
  • lastDelay: the previous delay, undefined when no delay has been computed yet.
  • lastResult: the last result, undefined if the last call to fn failed
  • lastError: the last error, undefined if the last call to fn succeeded

Until family

retry comes with handy utility functions for common use cases:

UntilDefined : To retry until we get a value which is neither null nor undefined.

For calling sync function:

retryUntilDefined<RETURN_TYPE>(
  fn: () => RETURN_TYPE | undefined | null,
  retryOptions?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryUntilDefinedDecorator<PARAMETERS_TYPE, RETURN_TYPE>(
  fn: (...args: PARAMETERS_TYPE) => RETURN_TYPE | undefined | null,
  retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>

For calling async function:

retryAsyncUntilDefined<RETURN_TYPE>(
  fn: () => Promise<RETURN_TYPE | undefined | null>,
  options?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryAsyncUntilDefinedDecorator<PARAMETERS_TYPE, RETURN_TYPE>(
  fn: (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE | undefined | null>,
  retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>

UntilTruthy : To retry until we get a value which JavaScript considers truthy.

For calling sync function:

retryUntilTruthy<PARAMETERS_TYPE, RETURN_TYPE>(
  fn: (...args: PARAMETERS_TYPE) => RETURN_TYPE,
  retryOptions?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryUntilTruthyDecorator<PARAMETERS_TYPE, RETURN_TYPE>(
  fn: (...args: PARAMETERS_TYPE) => RETURN_TYPE,
  retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>

For calling async function:

retryAsyncUntilTruthy<PARAMETERS_TYPE, RETURN_TYPE>(
  fn: (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>,
  retryOptions?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryAsyncUntilTruthyDecorator<PARAMETERS_TYPE, RETURN_TYPE>(
  fn: (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>,
  retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>

UntilResponse : To retry until fetch is successful.

retryAsyncUntilResponse<PARAMETERS_TYPE, RETURN_TYPE extends { ok: boolean }>(
  fn: () => Promise<RETURN_TYPE>,
  retryOptions?: RetryUtilsOptions,
): Promise<RETURN_TYPE>
retryAsyncUntilResponseDecorator<PARAMETERS_TYPE, RETURN_TYPE extends { ok: boolean }>(
  fn: (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>,
  retryOptions?: RetryUtilsOptions,
): (...args: PARAMETERS_TYPE) => Promise<RETURN_TYPE>

RetryUtilsOptions type is the same as RetryOptions but without the until option.

Delay family

createExponentialDelay Returns a delay function that provides exponential delays.

const delay = createExponentialDelay(20);
const result = await retryAsync(
  async () => {
    /* do something */
  },
  { delay, maxTry: 5 },
);

delay between each try will return 20, 400, 8000, 160000, 3200000

createMultiplicativeDelay Returns a delay function that provides multiplicative delays:

createMultiplicativeDelay<RETURN_TYPE>(initialDelay: number, multiplicator: number)
createMultiplicableDelay<RETURN_TYPE>(initialDelay: number, multiplier: number)

First delay returns initialDelay, second initialDelay*multiplicator, third multiplicator * initialDelay _ (multiplicator _ 2) and so on

const delay = createMultiplicableDelay(20);
const result = await retryAsync(
  async () => {
    /* do something */
  },
  { delay, maxTry: 5 },
);

delay will be 20, 60, 120, 180, 240

createRandomDelay Returns a delay function that provides random delays between given min and max (included):

createRandomDelay<RETURN_TYPE>(min: number, max: number)

Each time the created delay is called, a value between min and max (both included) is generated

const delay = createRandomDelay(500, 10000);
const result = await retryAsync(
  async () => {
    /* do something */
  },
  { delay, maxTry: 5 },
);

delay between each try will be a random value between 500 and 10000 ms.

Wait family

  • wait(duration?): Does nothing for "duration" milliseconds
  • waitUntil(fn, duration?, error?): waitUntil calls fn asynchronously once. If fn completes within the duration (expressed in milliseconds), waitUntil returns the fn result. Otherwise, it throws the given error (if any) or a TimeoutError exception.
  • waitUntilAsync(fn, duration?, error?): same as waitUntil, except fn is an asynchronous function.
  • TimeoutError: an error thrown by waitUntil and waitUntilAsync. It comes with an isTimeoutError type guard:
if (isTimeoutError(error)) {
  // fn does not complete within 10 seconds
}

In case of timeout fn is still executing. It is advised to add a means to abort it.

  • When duration is not provided, the default one is applied. The default is 60000ms.
  • setDefaultDuration(duration: number): change the default duration.
  • getDefaultDuration(): returns the current default duration.
  • waitUntilAsyncDecorator(fn: T, duration?: number, error?: Error) and waitUntilDecorator(fn: T, duration?: number, error?: Error): decorators that return a function with the same signature as the given function. On decorated call, fn is called bound to the duration.

Custom reaction when max retry is achieved

Sometimes, you need to perform some actions when max retry is achieved and the error is still there. For this onMaxRetryFunc?: (err: Error, currentTry: number) => void; optional function was added to RetryOptions. For example, you would like to store results of the error into the file in order to process it later. Here's how you can do it :

export const runWithRetry = <T>(
  message: string,
  serviceUnderTest: ServiceUnderTest,
  fn: () => T | Promise<T>,
  delay = 1000,
  maxTry = 10,
) => {
  const saveErrorReport = (err, currentTry) => {
    const errorDetails = {
      serviceName: serviceUnderTest.connectorName,
      error: err.message as string,
      description: `Failed to ${message} because of ${err.message as string}`,
      errorName: err.name as string,
      stack: err.stack as string,
      currentTry,
    };
    const path = resolve(
      __dirname,
      `../../../failed-service-report/${serviceUnderTest.connectorName}.json`,
    );
    writeFile(path, Buffer.from(JSON.stringify(errorDetails)));
  };
  return retryAsync(
    async () => {
      logger.info(`${serviceUnderTest.description}: ${message}`);
      return fn();
    },
    {
      delay,
      maxTry,
      onMaxRetryFunc: saveErrorReport,
    },
  );
};

Compatibility

This lib works with Deno (to import it, use the URL https://raw.githubusercontent.com/franckLdx/ts-retry/<version>/src/index.ts). However, it's more convenient to use the specific port of this lib to Deno: see https://deno.land/x/retry