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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@yveskaufmann/retry

v1.1.2

Published

Utility for retrying promise based operation on certain situations.

Downloads

1,461

Readme

@yveskaufmann/retry - retry-utility

npm version Node.js CI codecov CodeQL

Utility for retrying promise based operations when a certain response or error is returned.

  • Provides an imperative API for non class methods.
  • Provides Class decorators to mark async functions as retirable.
  • Supports various backoff strategies: fixed, linear, exponential.

Installation

npm install @yveskaufmann/retry

Usage

The examples below show how to annotate methods to mark them as retirable. If retryWhen returns true then the method will retried. The limit of retries is given by maxRetries.

When the retry limit is reached, the last return value is returned or the last thrown error is thrown.

NOTE: The Retryable annotation only works with methods that return promise like async methods.

class Service {

  @Retryable({
    retryWhen: (result, err) => err != null,
    delay: Retry.Delays.linear(50),
    maxRetries: 3,
  })
  async retryOnAnyError() {
    // ...
  }

  @Retryable({
    retryWhen: (result, err) => err == null && result == false,
    delay: Retry.Delays.linear(50),
    maxRetries: 3,
  })
  async retryWhenFalseIsReturned() {
    return Math.random() < 0.6;
  }
}

This utility can also be used with async functions:

 const response = await Retry.do({
  operation: () => client.get(...),
  retryWhen: (result, err) => HttpError.isTooManyRequest(err)
  maxRetries: 3,
  delay: Retry.Delays.constant(50),
});

API Reference

RetryOptions

/**
 * Options to configure a retriable operation.
 */
interface RetryOptions<T> {
  /**
   * The maximum amount of times a operation should be re-tried. 
   * Excluding the *initial attempt.
   */
  maxRetries?: number;

  /**
   * When set to true a MaxRetryAttemptsReached will be thrown if 
   * the retry attempts limit is reached.
   *
   * You should not use this option, 
   * if you are interested into result/error that was produced 
   * by the operation.
   */
  throwMaxAttemptError?: boolean;

  /**
   * Helps to identify the retryable operation in errors 
   * and logs.
   */
  nameOfOperation?: string;

  /**
   * The max number of milliseconds between two retries. Default is `Unlimited`
   */
  maxDelay?: number;

  /**
   * Defines the condition on which a operation should be 
   * retried, a implementation should return true 
   * if a retry is desired.
   *
   * @param result The result from the current attempt, 
   * can be null if no error was thrown by the operation.
   * 
   * @param error  The error that was thrown by the operation 
   * during the latest attempt.
   *
   * @returns True if a retry should be done.
   *
   */
  retryWhen: (result: T, err: Error) => boolean;

  /**
   * Calculates the delay between retries in milliseconds.
   *
   * @param attempts Count of failed attempts
   * 
   * @param error The error that was thrown by the operation 
   *  during the previous attempt.
   *
   * @returns A delay duration in  milliseconds.
   */
  delay?: (attempts: number, error?: Error) => number;

 /**
   * Will be invoked after each failed attempt.
   * An atempt is to be considered failed, if the `retryWhen` classify a returned error/return-values as retryable.

   * @param attempt The number of the attempt.
   * @param attemptsLeft The number of attempts left.

   * @param result The returned value from the retryable operation, that is considered be a reason to perform a retry
   * @param err The cause of the failed attempt
   * @returns
   */
  onFailedAttempt?: (attempt: number, attemptsLeft: number, result: T, err: Error) => void;
}

Retry

This is the retry class that provides access to built-in delay(backoff) strategies and built-in retry conditions.

class Retry {
  /**
   * List of built-in delays
   */
  public static readonly Delays = new Delays();

  /**
   * List of built-in retry conditions
   */
  public static readonly Conditions = new Conditions();
 
 /**
   * The do function invokes a given operation,
   * and will re-invoke this operation if 
   * @link{RetryOptions#retryWhen} returns true.
   * (@link{RetryOptions#retryWhen} will be provided with the operations result)
   * 
   * The operation will not be further retried if the 
   * @link{RetryOptions#maxRetries} limit is reached, 
   * in this case either the return value from the previous 
   * operation invocation is returned or the error that
   * was last thrown by the invoked operation will be thrown.
   *
   * @param options Configuration of the retry options
   *
   * @throws MaxRetryAttemptsReached if the retry attempt limit 
   * is reached and the option @link{RetryOptions#throwMaxAttemptError} 
   * was enabled  otherwise the error from the last invocation 
   * will be thrown.
   *
   * @returns The result of the last operation invocation.
   */
  public static async do<T>(options: RetryOptions<T>): Promise<T>;
}

RetryDelays

/**
 * List of built-in delay strategies.
 */
class Delays {
  /**
   * No delay between retries
   */
  public none();

  /**
   * Constant delay in milliseconds between retries.
   */
  public constant(delayInMs: number);

  /**
   * Linear sloping delay per attempt
   */
  public linear(delayInMs: number);
  
  /**
   * Quadratic sloping delays per attempt
   */
  public potential(delayInMs: number);
}

A custom delay strategy can be created by implementing a function that fulfills the interface below:

  /**
   * Calculates the delay between retries in milliseconds.
   *
   * @param attempts Count of failed attempts
   * @param error The error that was thrown by the operation 
   * during the previous attempt.
   *
   * @returns A delay duration in  milliseconds.
   */
  () => (attempts: number, error?: Error) => number

RetryConditions

/**
 * List of built-in retry conditions.
 */
class Conditions {
  /**
   * Retries a operation on any thrown error.
   */
  public onAnyError();

  /**
   * Retries a operation in any case.
   */
  public always();

  /**
   * Retries a operation if it returned a nullthy value.
   */
  public onNullResult();
}

A custom retry-condition can be created by implementing a function that fulfills the following interface:

 /**
   * Defines the condition on which a operation should be retried a implementation
   * should return true if a retry is desired.
   *
   * @param result The result from the current attempt, can be null if no error was thrown by the operation.
   * @param error  The error that was thrown by the operation during the current attempt.
   *
   * @returns True if a retry should be done.
   *
   */
  retryWhen: (result: T, err: Error) => boolean;

Retryable

/**
 * This method decorator marks a method as retriable.
 *
 * It uses the same options as @{link Retry#do} with the exception
 * that the operation is the annotated method.
 *
 * NOTE: That the annotated method has to be async or it should at least
 * return a promise.
 *
 * @param options Configuration of the retry options
 */
function Retryable(options: RetryOptions<unknown>): MethodDecorator

MaxRetryAttemptsReached

/**
 * This error will be thrown if the maximum retry attempts of a
 * operation is reached buf only if @{link RetryOptions#throwMaxAttemptError} was set to true.
 */
class MaxRetryAttemptsReached extends Error {
  /**
   * Can contain the cause of this error,
   * which follows the specs of Error Cause proposal:
   *
   * https://tc39.es/proposal-error-cause/
   */
  public readonly cause: Error;

  constructor(message: string, cause?: Error);
}