p-retry

repository·main·Indexed 21 days ago

https://github.com/sindresorhus/p-retry

A utility for retrying promise-returning or async functions using exponential backoff. Version 8.0.0 provides fine-grained control over retry strategies via options such as retries, factor, minTimeout, and maxTimeout, as well as lifecycle hooks like onFailedAttempt, shouldRetry, and shouldConsumeRetry. It includes the makeRetriable function to wrap functions for automatic retries and the AbortError class to immediately stop the retry loop.

Tokens
3K
Snippets
11
Records
17
Agent score
74%

What's inside p-retry

  1. Stop retries on SIGINT (Ctrl+C)

    main

    To stop retries when the process receives a signal like SIGINT, use an AbortController 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);
    }
  2. Configure pRetry options

    main

    The options object allows you to customize the retry behavior:

    • onFailedAttempt(context): Callback invoked on each failure. Receives a context object. Called after shouldConsumeRetry and before shouldRetry (except for AbortError).
    • shouldRetry(context): Return true to retry, false to abort. Called after onFailedAttempt and shouldConsumeRetry.
    • shouldConsumeRetry(context): Return false to prevent the failure from consuming a retry from the retries budget. Called before onFailedAttempt and shouldRetry.
    • retries: Max number of retries (default: 10).
    • factor: Exponential factor (default: 2).
    • minTimeout: Milliseconds before first retry (default: 1000).
    • maxTimeout: Max milliseconds between retries (default: Infinity).
    • randomize: Whether to randomize timeouts (default: false).
    • maxRetryTime: Max time (ms) the operation is allowed to run (default: Infinity).
    • signal: An AbortSignal to cancel retries.
    • unref: If true, prevents retry timeouts from keeping the process alive (Node.js only).
  3. Understand the RetryContext object

    main

    The RetryContext object is passed to onFailedAttempt, shouldRetry, and shouldConsumeRetry. It provides information about the current state of the retry loop:

    • error: The Error that caused the current failure.
    • attemptNumber: The current attempt number (starting at 1).
    • retriesLeft: How many retries remain in the budget.
    • retriesConsumed: How many retries have been used so far.
    • retryDelay: The calculated delay in milliseconds before the next attempt. This is 0 if the retry is skipped or no retry will occur.
  4. Basic usage of pRetry

    main

    Use pRetry to wrap an async function or a function that returns a promise. If the function fails, p-retry will retry it using exponential backoff until the maximum number of retries is reached or an error occurs that should abort the process.

    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}));
  5. Pass arguments to the retried function

    main

    To pass arguments to the function being retried, wrap it in an inline arrow function.

    import pRetry from 'p-retry';
    
    const run = async emoji => {
    	// ...
    };
    
    // With arguments
    await pRetry(() => run('🦄'), {retries: 5});
  6. makeRetriable(function, options?)

    main

    Wraps a function so that every call to it is automatically retried on failure.

    import {makeRetriable} from 'p-retry';
    
    const fetchWithRetry = makeRetriable(fetch, {retries: 5});
    
    const response = await fetchWithRetry('https://sindresorhus.com/unicorn');
  7. pRetry(input, options?)

    main

    Returns a Promise that is fulfilled when calling input returns a fulfilled promise. If input rejects, it is called again until max retries are reached, then it rejects with the last rejection reason.

    Note on TypeErrors: p-retry does not retry on most TypeErrors (except network errors) to avoid infinite loops on logic errors. Non-network TypeErrors will always abort retries.

  8. Configure pRetry with Options

    main

    The Options object allows you to control the retry strategy, including backoff timing, retry limits, and lifecycle hooks.

    Lifecycle Hooks

    • onFailedAttempt: Invoked on each failure. Receives a RetryContext. Can be used for logging or adding custom delays (e.g., using delay).
    • shouldConsumeRetry: Decides if a failure should count against the retries budget. If false, the retry count is not decremented and backoff is not incremented.
    • shouldRetry: Decides if a retry should occur based on the error. Returning false aborts retries immediately.

    Timing and Limits

    • retries: Maximum number of retries (default: 10).
    • minTimeout: Milliseconds before the first retry (default: 1000).
    • maxTimeout: Maximum milliseconds between retries (default: Infinity).
    • factor: Exponential factor for backoff (default: 2).
    • randomize: If true, multiplies timeouts by a factor between 1 and 2 (default: false).
    • maxRetryTime: Maximum total time allowed for all retries (default: Infinity).

    Other

    • signal: An AbortSignal to allow external cancellation via an AbortController.
    • unref: If true, prevents retry timeouts from keeping the Node.js process alive.
  9. The onFailedAttempt callback context

    main

    The context object passed to onFailedAttempt contains:

    • error: The error that was thrown
    • attemptNumber: The attempt number (starts at 1)
    • retriesLeft: Number of retries remaining
    • retriesConsumed: Number of retries consumed so far
    • retryDelay: Delay in ms before the next retry (0 if skipped or no retry will occur).
  10. Abort retries using AbortError

    main

    You can stop the retry loop and immediately reject the promise by throwing an AbortError. This is useful when you encounter a specific error condition where retrying is known to be futile (e.g., a 404 Not Found).

    import pRetry, {AbortError} from 'p-retry';
    
    const run = async () => {
    	const response = await fetch('https://sindresorhus.com/unicorn');
    
    	if (response.status === 404) {
    		throw new AbortError(response.statusText);
    	}
    
    	return response.blob();
    };
    
    await pRetry(run);