or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

Async Retry

Async Retry provides a simple, promise-based retry mechanism for async and sync functions with configurable backoff strategies. Built as a wrapper around the 'retry' npm package, it offers an intuitive API with intelligent retry logic, including exponential backoff, maximum retry attempts, and the ability to abort retries when certain conditions are met.

Package Information

  • Package Name: async-retry
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install async-retry

Core Imports

const retry = require("async-retry");

Note: This package uses CommonJS format only. For ES modules, use dynamic import:

const retry = (await import("async-retry")).default;

Basic Usage

const retry = require("async-retry");
const fetch = require("node-fetch");

// Retry an async operation with default settings
await retry(async (bail, num) => {
  const res = await fetch("https://api.example.com/data");
  
  if (res.status === 403) {
    // Don't retry authentication errors
    bail(new Error("Unauthorized"));
    return;
  }
  
  if (!res.ok) {
    throw new Error(`HTTP ${res.status}`);
  }
  
  return await res.json();
});

// Retry with custom options
await retry(
  async (bail, num) => {
    console.log(`Attempt ${num}`);
    const result = await someAsyncOperation();
    return result;
  },
  {
    retries: 5,
    minTimeout: 500,
    factor: 2,
    onRetry: (err, num) => {
      console.log(`Retry ${num} due to:`, err.message);
    }
  }
);

Capabilities

Retry Function

The main retry function that wraps any function (sync or async) with configurable retry logic.

/**
 * Retry a function with exponential backoff and configurable options
 * @param {Function} fn - Function to retry (receives bail and attempt number)
 * @param {Object} opts - Options object (optional)
 * @returns {Promise} Promise that resolves to the return value of fn
 */
function retry(fn, opts);

Parameters:

  • fn (Function): The function to retry
    • Receives bail (Function) as first parameter to abort retrying
    • Receives num (Number) as second parameter indicating attempt number (starts at 1)
    • Can be sync or async function
    • Should throw/reject to trigger retry, or call bail() to abort
  • opts (Object, optional): Configuration options passed to node-retry

Returns: Promise that resolves to the return value of fn or rejects with the final error

Retry Options

Configuration object for customizing retry behavior.

/**
 * Configuration object for customizing retry behavior
 * @typedef {Object} RetryOptions
 * @property {number} [retries=10] - Maximum number of retry attempts
 * @property {number} [factor=2] - Exponential backoff factor
 * @property {number} [minTimeout=1000] - Initial retry delay in milliseconds
 * @property {number} [maxTimeout=Infinity] - Maximum delay between retries in milliseconds
 * @property {boolean} [randomize=true] - Add randomization to timeouts
 * @property {function} [onRetry] - Callback invoked after each retry attempt (err, attempt) => void
 */

Bail Function

Function provided as the first parameter to the retry function for aborting retry attempts.

/**
 * Abort the retry mechanism immediately
 * @param {Error} err - Error to reject the retry promise with
 */
function bail(err);

Usage Examples:

// Bail on authentication errors
await retry(async (bail, num) => {
  const res = await fetch(url, { headers: { auth: token } });
  
  if (res.status === 401 || res.status === 403) {
    bail(new Error("Authentication failed"));
    return;
  }
  
  return await res.json();
});

// Bail using error property
await retry(async (bail, num) => {
  try {
    return await riskyOperation();
  } catch (err) {
    if (err.code === "PERMANENT_FAILURE") {
      err.bail = true; // Alternative way to bail
      throw err;
    }
    throw err; // Will retry
  }
});

Error Handling

Bail Mechanisms

Two ways to abort retrying:

  1. Explicit bail call: Call bail(error) within the retry function
  2. Error property: Set error.bail = true on thrown errors
await retry(async (bail, num) => {
  try {
    return await operation();
  } catch (err) {
    // Method 1: Explicit bail
    if (err.code === "FATAL_ERROR") {
      bail(err);
      return;
    }
    
    // Method 2: Error property
    if (err.code === "AUTH_ERROR") {
      err.bail = true;
      throw err;
    }
    
    // Regular throw will trigger retry
    throw err;
  }
});

Error Propagation

  • If retries are exhausted, the promise rejects with the last error
  • If bail() is called, the promise rejects with the provided error
  • If an error has bail: true property, retrying stops immediately

Usage Patterns

Synchronous Functions

// Wrap sync functions
const result = await retry((bail, num) => {
  if (Math.random() < 0.7) {
    throw new Error("Random failure");
  }
  return "Success!";
}, { retries: 3 });

HTTP Requests with Conditional Retry

const retry = require("async-retry");
const fetch = require("node-fetch");

const data = await retry(
  async (bail, num) => {
    const res = await fetch("https://api.example.com/data");
    
    // Don't retry client errors (4xx)
    if (res.status >= 400 && res.status < 500) {
      bail(new Error(`Client error: ${res.status}`));
      return;
    }
    
    // Retry server errors (5xx) and network issues
    if (!res.ok) {
      throw new Error(`Server error: ${res.status}`);
    }
    
    return await res.json();
  },
  {
    retries: 5,
    minTimeout: 1000,
    maxTimeout: 5000,
    onRetry: (err, attempt) => {
      console.log(`Attempt ${attempt} failed:`, err.message);
    }
  }
);

Database Operations

const result = await retry(
  async (bail, num) => {
    try {
      return await db.query("SELECT * FROM users");
    } catch (err) {
      // Don't retry syntax errors
      if (err.code === "SYNTAX_ERROR") {
        bail(err);
        return;
      }
      // Retry connection issues
      throw err;
    }
  },
  {
    retries: 3,
    factor: 1.5,
    minTimeout: 2000
  }
);

Custom Backoff Strategy

// Custom exponential backoff with jitter
await retry(operation, {
  retries: 10,
  factor: 2,           // Double the timeout each time
  minTimeout: 100,     // Start with 100ms
  maxTimeout: 30000,   // Cap at 30 seconds
  randomize: true      // Add randomization (default)
});