Top Related Projects
Retrying made simple, easy and async
Abstraction for exponential and custom retry strategies for failed operations.
Axios plugin that intercepts failed requests and retries them whenever possible
Quick Overview
p-retry is a JavaScript library that provides retry functionality for promises. It allows you to automatically retry failed operations with customizable retry strategies, including exponential backoff. This library is particularly useful for handling network requests or other operations that may fail intermittently.
Pros
- Easy to use and integrate into existing promise-based code
- Supports customizable retry strategies, including exponential backoff
- Provides TypeScript support for better type checking and IDE integration
- Lightweight with no external dependencies
Cons
- Limited to promise-based operations
- May not be suitable for complex retry scenarios that require more advanced logic
- Could potentially mask underlying issues if used incorrectly
- Requires careful consideration of retry limits to avoid excessive retries
Code Examples
- Basic usage with default options:
import pRetry from 'p-retry';
const result = await pRetry(async () => {
// Attempt some operation that might fail
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('API request failed');
return response.json();
});
- Custom retry options:
import pRetry from 'p-retry';
const result = await pRetry(
async () => {
// Attempt some operation that might fail
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('API request failed');
return response.json();
},
{
retries: 5,
onFailedAttempt: error => {
console.log(`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`);
}
}
);
- Using with AbortController:
import pRetry from 'p-retry';
const abortController = new AbortController();
try {
const result = await pRetry(
async () => {
const response = await fetch('https://api.example.com/data', {
signal: abortController.signal
});
if (!response.ok) throw new Error('API request failed');
return response.json();
},
{ signal: abortController.signal }
);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Operation was aborted');
} else {
console.error('Operation failed after retries:', error);
}
}
Getting Started
To use p-retry in your project, first install it using npm:
npm install p-retry
Then, import and use it in your JavaScript/TypeScript code:
import pRetry from 'p-retry';
async function fetchData() {
return pRetry(async () => {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('API request failed');
return response.json();
}, { retries: 3 });
}
fetchData().then(data => console.log(data)).catch(error => console.error(error));
This example demonstrates how to use p-retry to fetch data from an API with up to 3 retry attempts.
Competitor Comparisons
Retrying made simple, easy and async
Pros of async-retry
- Simpler API with fewer options, making it easier to use for basic retry scenarios
- Built-in support for custom retry strategies through the
retriesoption - Lightweight package with minimal dependencies
Cons of async-retry
- Less flexible than p-retry in terms of customization options
- Lacks built-in support for exponential backoff (though it can be implemented manually)
- Does not provide as many advanced features, such as abort control or custom error handling
Code Comparison
p-retry:
import pRetry from 'p-retry';
await pRetry(async () => {
// Attempt task here
}, { retries: 5 });
async-retry:
import retry from 'async-retry';
await retry(async (bail) => {
// Attempt task here
}, { retries: 5 });
Both libraries offer similar basic functionality for retrying asynchronous operations. p-retry provides more advanced features and customization options, while async-retry offers a simpler API for basic retry scenarios. The choice between the two depends on the specific requirements of your project and the level of control you need over the retry process.
Abstraction for exponential and custom retry strategies for failed operations.
Pros of node-retry
- More flexible retry strategies with customizable backoff algorithms
- Supports both callback and promise-based approaches
- Includes built-in timeout functionality
Cons of node-retry
- Less actively maintained compared to p-retry
- Lacks TypeScript support out of the box
- More complex API, potentially steeper learning curve
Code Comparison
node-retry:
var retry = require('retry');
var operation = retry.operation({
retries: 5,
factor: 3,
minTimeout: 1 * 1000,
maxTimeout: 60 * 1000,
randomize: true,
});
operation.attempt(function(currentAttempt) {
// ... attempt operation
});
p-retry:
const pRetry = require('p-retry');
await pRetry(async () => {
// ... attempt operation
}, {
retries: 5
});
Both libraries provide retry functionality, but node-retry offers more granular control over retry behavior at the cost of a more verbose API. p-retry, on the other hand, provides a simpler, promise-based interface that integrates well with modern JavaScript practices.
While node-retry supports both callbacks and promises, p-retry is designed specifically for promise-based operations, making it more suitable for projects using async/await syntax. However, node-retry's flexibility in retry strategies and timeout handling can be advantageous in complex scenarios where fine-tuned control is necessary.
Axios plugin that intercepts failed requests and retries them whenever possible
Pros of axios-retry
- Specifically designed for Axios, providing seamless integration
- Offers more granular control over retry behavior for HTTP requests
- Includes built-in exponential backoff strategy
Cons of axios-retry
- Limited to Axios library, not as versatile for other use cases
- Less customizable retry logic compared to p-retry
- Smaller community and fewer updates
Code Comparison
p-retry:
import pRetry from 'p-retry';
await pRetry(() => fetchSomething(), { retries: 5 });
axios-retry:
import axios from 'axios';
import axiosRetry from 'axios-retry';
axiosRetry(axios, { retries: 3 });
axios.get('https://example.com');
Key Differences
- Scope: p-retry is a general-purpose retry utility, while axios-retry is specific to Axios HTTP requests.
- Configuration: axios-retry is configured globally for an Axios instance, whereas p-retry is applied to individual function calls.
- Flexibility: p-retry can wrap any asynchronous function, making it more versatile for various scenarios beyond HTTP requests.
- Error handling: p-retry provides more control over which errors trigger retries, while axios-retry focuses on HTTP-specific error conditions.
- Community: p-retry has a larger user base and more frequent updates, potentially offering better long-term support and maintenance.
Both libraries serve their purposes well, with p-retry offering more flexibility for general use cases and axios-retry providing a tailored solution for Axios users.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
p-retry
Retry a promise-returning or async function
It does exponential backoff and supports custom retry strategies for failed operations.
Install
npm install p-retry
Usage
import pRetry, {AbortError} from 'p-retry';
const run = async () => {
const response = await fetch('https://sindresorhus.com/unicorn');
// Abort retrying if the resource doesn't exist
if (response.status === 404) {
throw new AbortError(response.statusText);
}
return response.blob();
};
console.log(await pRetry(run, {retries: 5}));
API
pRetry(input, options?)
Returns a Promise that is fulfilled when calling input returns a fulfilled promise. If calling input returns a rejected promise, input is called again until the max retries are reached, it then rejects with the last rejection reason.
Does not retry on most TypeErrors, with the exception of network errors. This is done on a best case basis as different browsers have different messages to indicate this. See whatwg/fetch#526 (comment)
Non-network TypeErrors always abort retries, even if shouldConsumeRetry or shouldRetry would otherwise allow another attempt.
input
Type: Function
Receives the number of attempts as the first argument and is expected to return a Promise or any value.
options
Type: object
onFailedAttempt(context)
Type: Function
Callback invoked on each failure. Receives a context object containing the error and retry state information.
The function is called after shouldConsumeRetry and before shouldRetry, for all errors except AbortError.
If the function throws, all retries will be aborted and the original promise will reject with the thrown error.
import pRetry from 'p-retry';
const run = async () => {
const response = await fetch('https://sindresorhus.com/unicorn');
if (!response.ok) {
throw new Error(response.statusText);
}
return response.json();
};
const result = await pRetry(run, {
onFailedAttempt: ({error, attemptNumber, retriesLeft, retriesConsumed, retryDelay}) => {
console.log(`Attempt ${attemptNumber} failed. Retrying in ${retryDelay}ms. ${retriesLeft} retries left.`);
// 1st request => Attempt 1 failed. Retrying in 1000ms. 5 retries left.
// 2nd request => Attempt 2 failed. Retrying in 2000ms. 4 retries left.
// â¦
},
retries: 5
});
console.log(result);
The context object contains:
error- The error that was thrownattemptNumber- The attempt number (starts at 1)retriesLeft- Number of retries remainingretriesConsumed- Number of retries consumed so farretryDelay- The delay in milliseconds before the next retry (based onminTimeout,factor,maxTimeout, andrandomize). This is0when the retry is skipped or when no retry will occur based on the checks completed before the current callback runs.
The onFailedAttempt function can return a promise. For example, to add a delay:
import pRetry from 'p-retry';
import delay from 'delay';
const run = async () => { ⦠};
const result = await pRetry(run, {
onFailedAttempt: async () => {
console.log('Waiting for 1 second before retrying');
await delay(1000);
}
});
shouldRetry(context)
Type: Function
Decide if a retry should occur based on context. Returning true triggers a retry, false aborts with the error.
The function is called after onFailedAttempt and shouldConsumeRetry.
The function is not called on AbortError, TypeError (except network errors), or if retries or maxRetryTime are exhausted.
If the function throws, all retries will be aborted and the original promise will reject with the thrown error.
import pRetry from 'p-retry';
const run = async () => { ⦠};
const result = await pRetry(run, {
shouldRetry: ({error, attemptNumber, retriesLeft}) => !(error instanceof CustomError)
});
In the example above, the operation will be retried unless the error is an instance of CustomError.
shouldConsumeRetry(context)
Type: Function
Decide if this failure should consume a retry from the retries budget.
When false is returned, the failure will not consume a retry or increment backoff values, but is still subject to maxRetryTime.
The function is called before onFailedAttempt and shouldRetry.
The function is not called on AbortError.
If the function throws, all retries will be aborted and the original promise will reject with the thrown error.
import pRetry from 'p-retry';
const run = async () => { ⦠};
const result = await pRetry(run, {
retries: 2,
shouldConsumeRetry: ({error, retriesLeft}) => !(error instanceof RateLimitError),
});
In the example above, RateLimitErrors will not decrement the available retries.
retries
Type: number
Default: 10
The maximum amount of times to retry the operation.
factor
Type: number
Default: 2
The exponential factor to use.
minTimeout
Type: number
Default: 1000
The number of milliseconds before starting the first retry.
Set this to 0 to retry immediately with no delay.
maxTimeout
Type: number
Default: Infinity
The maximum number of milliseconds between two retries.
randomize
Type: boolean
Default: false
Randomizes the timeouts by multiplying with a factor between 1 and 2.
maxRetryTime
Type: number
Default: Infinity
The maximum time (in milliseconds) that the retried operation is allowed to run.
Measured with a monotonic clock (performance.now()) so system clock adjustments do not affect the limit.
signal
Type: AbortSignal
You can abort retrying using AbortController.
import pRetry from 'p-retry';
const run = async () => { ⦠};
const controller = new AbortController();
cancelButton.addEventListener('click', () => {
controller.abort(new Error('User clicked cancel button'));
});
try {
await pRetry(run, {signal: controller.signal});
} catch (error) {
console.log(error.message);
//=> 'User clicked cancel button'
}
unref
Type: boolean
Default: false
Prevents retry timeouts from keeping the process alive.
Only affects platforms with a .unref() method on timeouts, such as Node.js.
makeRetriable(function, options?)
Wrap a function so that each call is automatically retried on failure.
import {makeRetriable} from 'p-retry';
const fetchWithRetry = makeRetriable(fetch, {retries: 5});
const response = await fetchWithRetry('https://sindresorhus.com/unicorn');
AbortError(message)
AbortError(error)
Abort retrying and reject the promise. No callback functions will be called.
message
Type: string
An error message.
error
Type: Error
A custom error.
Tip
You can pass arguments to the function being retried by wrapping it in an inline arrow function:
import pRetry from 'p-retry';
const run = async emoji => {
// â¦
};
// Without arguments
await pRetry(run, {retries: 5});
// With arguments
await pRetry(() => run('ð¦'), {retries: 5});
FAQ
How do I mock timers when testing with this package?
The package uses setTimeout and clearTimeout from the global scope, so you can use the Node.js test timer mocking or a package like sinon.
How do I stop retries when the process receives SIGINT (Ctrl+C)?
Use an AbortController to signal cancellation on SIGINT, and pass its signal to pRetry:
import pRetry from 'p-retry';
const controller = new AbortController();
process.once('SIGINT', () => {
controller.abort(new Error('SIGINT received'));
});
try {
await pRetry(run, {signal: controller.signal});
} catch (error) {
console.log('Retry stopped due to:', error.message);
}
The package does not handle process signals itself to avoid global side effects.
Related
Top Related Projects
Retrying made simple, easy and async
Abstraction for exponential and custom retry strategies for failed operations.
Axios plugin that intercepts failed requests and retries them whenever possible
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot