Abstraction for exponential and custom retry strategies for failed operations.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Retry provides abstraction for exponential and custom retry strategies for failed operations. It offers both operation-based retry patterns through RetryOperation objects and functional wrapper patterns that can automatically add retry logic to existing functions.
npm install retryconst retry = require("retry");Note: This package uses CommonJS exports only. ES module syntax can be used with bundlers or transpilers:
import retry from "retry";Individual function imports:
const { operation, timeouts, createTimeout, wrap } = require("retry");const retry = require("retry");
function faultTolerantResolve(address, cb) {
const operation = retry.operation();
operation.attempt(function(currentAttempt) {
dns.resolve(address, function(err, addresses) {
if (operation.retry(err)) {
return;
}
cb(err ? operation.mainError() : null, addresses);
});
});
}Retry is built around several key components:
Create and manage retry operations with exponential backoff strategies. The core functionality for handling failed operations with configurable retry policies.
/**
* Creates a new RetryOperation instance with computed timeouts
* @param {Object} [options] - Configuration options for retry behavior
* @param {number} [options.retries=10] - Maximum number of retry attempts
* @param {number} [options.factor=2] - Exponential backoff factor
* @param {number} [options.minTimeout=1000] - Minimum timeout in milliseconds
* @param {number} [options.maxTimeout=Infinity] - Maximum timeout in milliseconds
* @param {boolean} [options.randomize=false] - Add randomization to timeouts
* @param {boolean} [options.forever=false] - Whether to retry forever
* @param {boolean} [options.unref=false] - Whether to unref setTimeout calls
* @param {number} [options.maxRetryTime=Infinity] - Maximum time allowed for retries in milliseconds
* @returns {RetryOperation} RetryOperation instance
*/
function operation(options);Usage Examples:
const retry = require("retry");
// Basic retry with default settings (10 retries, exponential backoff)
const operation = retry.operation();
// Custom retry configuration
const operation = retry.operation({
retries: 5,
factor: 3,
minTimeout: 1 * 1000,
maxTimeout: 60 * 1000,
randomize: true,
});
// Retry forever with maximum retry time limit
const operation = retry.operation({
forever: true,
maxRetryTime: 5 * 60 * 1000, // 5 minutes max
});Generate arrays of timeout values for exponential backoff strategies. Used internally by operations but also available for custom timeout management.
/**
* Generates array of timeout values for exponential backoff
* @param {Object|Array} [options] - Timeout configuration or array of timeout values
* @param {number} [options.retries=10] - Maximum number of retries
* @param {number} [options.factor=2] - Exponential backoff factor
* @param {number} [options.minTimeout=1000] - Minimum timeout in milliseconds
* @param {number} [options.maxTimeout=Infinity] - Maximum timeout in milliseconds
* @param {boolean} [options.randomize=false] - Add randomization to timeouts
* @param {boolean} [options.forever=false] - Enable infinite retries
* @returns {number[]} Array of timeout values in milliseconds
*/
function timeouts(options);
/**
* Calculates timeout value for specific attempt number
* @param {number} attempt - Zero-indexed attempt number
* @param {Object} opts - Options containing factor, minTimeout, maxTimeout, randomize
* @param {number} opts.factor - Exponential backoff factor
* @param {number} opts.minTimeout - Minimum timeout in milliseconds
* @param {number} opts.maxTimeout - Maximum timeout in milliseconds
* @param {boolean} [opts.randomize=false] - Add randomization to timeout
* @returns {number} Timeout value in milliseconds
*/
function createTimeout(attempt, opts);Usage Examples:
const retry = require("retry");
// Generate timeout array
const timeouts = retry.timeouts({
retries: 3,
factor: 2,
minTimeout: 1000,
maxTimeout: 5000,
randomize: true
});
// Result: [1000-2000, 2000-4000, 4000-5000] (randomized)
// Calculate specific timeout
const timeout = retry.createTimeout(2, {
factor: 2,
minTimeout: 1000,
maxTimeout: 10000
});
// Result: 4000 (1000 * 2^2)
// Use array of custom timeouts
const customTimeouts = retry.timeouts([100, 500, 1000, 2000]);Automatically add retry logic to existing object methods. Useful for wrapping APIs, database clients, or any object with methods that may fail.
/**
* Wraps object methods with retry logic
* @param {Object} obj - Object containing methods to wrap
* @param {Object|Array} [options] - Retry options or method names array
* @param {Array} [methods] - Array of method names to wrap
* @returns {void} Modifies obj in place
*/
function wrap(obj, options, methods);Usage Examples:
const retry = require("retry");
const dns = require("dns");
// Wrap all functions in an object
retry.wrap(dns);
// Wrap specific methods
retry.wrap(dns, ["resolve", "lookup"]);
// Wrap with custom retry options
retry.wrap(dns, {
retries: 3,
factor: 2,
minTimeout: 1000
});
// Wrap specific methods with options
retry.wrap(dns, {
retries: 5,
maxTimeout: 10000
}, ["resolve"]);
// The wrapped methods now automatically retry on errors
dns.resolve("example.com", function(err, addresses) {
// This will retry up to the configured number of times
console.log(err, addresses);
});Direct management of RetryOperation instances for fine-grained control over retry behavior and error handling.
/**
* RetryOperation constructor - manages individual retry operation lifecycle
* @param {number[]} timeouts - Array of timeout values in milliseconds
* @param {Object|boolean} [options] - Configuration options
* @param {boolean} [options.forever=false] - Whether to retry forever
* @param {boolean} [options.unref=false] - Whether to unref setTimeout calls
* @param {number} [options.maxRetryTime=Infinity] - Maximum time allowed for retries
*/
function RetryOperation(timeouts, options);
/**
* Executes function with retry capability
* @param {Function} fn - Function to execute, receives currentAttempt as parameter
* @param {Object} [timeoutOps] - Timeout configuration
* @param {number} [timeoutOps.timeout] - Operation timeout in milliseconds
* @param {Function} [timeoutOps.cb] - Timeout callback function
* @returns {void}
*/
RetryOperation.prototype.attempt = function(fn, timeoutOps);
/**
* Determines if operation should be retried based on error
* @param {Error|null} err - Error from operation attempt
* @returns {boolean} true if operation will be retried, false otherwise
*/
RetryOperation.prototype.retry = function(err);
/**
* Stops retry operation and clears timers
* @returns {void}
*/
RetryOperation.prototype.stop = function();
/**
* Resets operation to initial state for reuse
* @returns {void}
*/
RetryOperation.prototype.reset = function();
/**
* Returns array of all errors encountered during retries
* @returns {Error[]} Array of Error objects in chronological order
*/
RetryOperation.prototype.errors = function();
/**
* Returns current attempt count
* @returns {number} Number representing attempt count
*/
RetryOperation.prototype.attempts = function();
/**
* Returns most frequently occurring error
* @returns {Error|null} Error object or null if no errors
*/
RetryOperation.prototype.mainError = function();
/**
* @deprecated Use attempt() instead
* Deprecated alias for attempt()
* @param {Function} fn - Function to execute
* @returns {void}
*/
RetryOperation.prototype.try = function(fn);
/**
* @deprecated Use attempt() instead
* Deprecated alias for attempt()
* @param {Function} fn - Function to execute
* @returns {void}
*/
RetryOperation.prototype.start = function(fn);Usage Examples:
const retry = require("retry");
// Direct RetryOperation usage
const timeouts = retry.timeouts({ retries: 3 });
const operation = new retry.RetryOperation(timeouts);
operation.attempt(function(currentAttempt) {
console.log(`Attempt ${currentAttempt}`);
asyncOperation(function(err, result) {
if (err && err.message === 'FATAL_ERROR') {
// Stop retrying on fatal errors
operation.stop();
return callback(err);
}
if (operation.retry(err)) {
return; // Will retry
}
// Final result (success or max retries reached)
if (err) {
console.log('All errors:', operation.errors());
console.log('Main error:', operation.mainError());
console.log('Total attempts:', operation.attempts());
}
callback(operation.mainError(), result);
});
});
// Reset and reuse operation
operation.reset();
operation.attempt(/* ... */);/**
* Configuration options for retry operations
* @typedef {Object} RetryOptions
* @property {number} [retries=10] - Maximum number of retry attempts
* @property {number} [factor=2] - Exponential backoff factor
* @property {number} [minTimeout=1000] - Minimum timeout in milliseconds
* @property {number} [maxTimeout=Infinity] - Maximum timeout in milliseconds
* @property {boolean} [randomize=false] - Add randomization to timeouts
* @property {boolean} [forever=false] - Whether to retry forever
* @property {boolean} [unref=false] - Whether to unref setTimeout calls
* @property {number} [maxRetryTime=Infinity] - Maximum time allowed for retries in milliseconds
*/
/**
* Timeout operation configuration
* @typedef {Object} TimeoutOps
* @property {number} timeout - Operation timeout in milliseconds
* @property {Function} cb - Timeout callback function
*/The retry library provides comprehensive error handling and analysis:
errors()mainError() returns the most frequently occurring error based on error messagemaxRetryTime is exceededoperation.stop() to abort retries for unrecoverable errorsforever: true, only keeps the last error to prevent memory leaksThe timeout calculation uses the following formula:
timeout = Math.min(random * Math.max(minTimeout, 1) * Math.pow(factor, attempt), maxTimeout)Where random is 1 or a value between 1-2 if randomize is enabled. The Math.max(minTimeout, 1) ensures a minimum timeout of at least 1 millisecond.
const dns = require("dns");
const retry = require("retry");
function faultTolerantResolve(address, cb) {
const operation = retry.operation({
retries: 2,
factor: 2,
minTimeout: 1 * 1000,
maxTimeout: 2 * 1000,
randomize: true
});
operation.attempt(function(currentAttempt) {
dns.resolve(address, function(err, addresses) {
if (operation.retry(err)) {
return;
}
cb(operation.mainError(), operation.errors(), addresses);
});
});
}function apiCall(endpoint, data, callback) {
const operation = retry.operation({ retries: 3 });
operation.attempt(function(currentAttempt) {
httpRequest(endpoint, data, function(err, response) {
if (err && err.statusCode === 401) {
// Don't retry authentication errors
operation.stop();
return callback(err);
}
if (operation.retry(err)) {
return;
}
callback(operation.mainError(), response);
});
});
}const retry = require("retry");
// Wrap database client methods
retry.wrap(dbClient, {
retries: 5,
factor: 1.5,
minTimeout: 500,
maxTimeout: 10000
}, ["query", "connect"]);
// Now database operations automatically retry
dbClient.query("SELECT * FROM users", function(err, results) {
// This query will retry up to 5 times on failure
console.log(err, results);
});