Retrying made simple, easy and async
npx @tessl/cli install tessl/npm-async-retry@1.3.0Async 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.
npm install async-retryconst retry = require("async-retry");Note: This package uses CommonJS format only. For ES modules, use dynamic import:
const retry = (await import("async-retry")).default;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);
}
}
);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
bail (Function) as first parameter to abort retryingnum (Number) as second parameter indicating attempt number (starts at 1)bail() to abortopts (Object, optional): Configuration options passed to node-retryReturns: Promise that resolves to the return value of fn or rejects with the final error
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
*/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
}
});Two ways to abort retrying:
bail(error) within the retry functionerror.bail = true on thrown errorsawait 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;
}
});bail() is called, the promise rejects with the provided errorbail: true property, retrying stops immediately// Wrap sync functions
const result = await retry((bail, num) => {
if (Math.random() < 0.7) {
throw new Error("Random failure");
}
return "Success!";
}, { retries: 3 });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);
}
}
);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 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)
});