async-retry vs p-retry vs promise-retry vs retry vs retry-axios
Handling Transient Failures in JavaScript Applications
async-retryp-retrypromise-retryretryretry-axiosSimilar Packages:

Handling Transient Failures in JavaScript Applications

These libraries provide automated retry logic for operations that might fail temporarily, such as network requests or database connections. Instead of writing manual loops or complex error handling code, developers can use these tools to define how many times an action should be repeated and how long to wait between attempts. While they share a common goal, they differ in their API style β€” some use callbacks, others use Promises, and one is built specifically for the Axios HTTP client.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
async-retry01,915-305 years agoMIT
p-retry01,00725.5 kB1a month agoMIT
promise-retry0318-116 years agoMIT
retry01,259-205 years agoMIT
retry-axios050464.9 kB011 days agoApache-2.0

Retry Libraries Compared: async-retry vs p-retry vs promise-retry vs retry vs retry-axios

When building reliable software, things will fail β€” networks drop, APIs rate-limit, and databases lock. Instead of crashing, your application should try again. These five packages solve that problem, but they approach it in different ways. Let's look at how they handle API styles, configuration, error control, and specific use cases.

🧩 API Style: Callbacks vs Promises vs Interceptors

The biggest difference is how you write your code. Modern JavaScript uses async/await, but older tools rely on callbacks.

retry uses a callback pattern. You create an operation and pass a function that accepts a callback.

const retry = require('retry');

const operation = retry.operation();

operation.attempt(function(currentAttempt) {
  fetchData(function(err, result) {
    if (operation.retry(err)) {
      return;
    }
    console.log(operation.mainError());
  });
});

async-retry wraps this logic in a Promise. You pass an async function directly.

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

await retry(async (bail) => {
  const result = await fetchData();
  return result;
}, { retries: 3 });

p-retry also uses Promises but focuses on standard async functions.

const pRetry = require('p-retry');

await pRetry(async () => {
  const result = await fetchData();
  return result;
}, { retries: 3 });

promise-retry passes a retry function into your Promise. You decide when to call it.

const promiseRetry = require('promise-retry');

await promiseRetry((retry, number) => {
  return fetchData().catch(retry);
}, { retries: 3 });

retry-axios is different β€” it attaches to an Axios instance. You do not wrap calls manually.

const axios = require('axios');
const retry = require('retry-axios');

const client = axios.create();
retry.attach({ client });

// Normal call, but retries automatically on failure
await client.get('/api/data');

βš™οΈ Configuration: Attempts and Delays

All libraries let you set how many times to try and how long to wait. Most use exponential backoff β€” waiting longer after each failure.

retry configures timeouts on the operation object.

const operation = retry.operation({
  retries: 3,
  factor: 2,
  minTimeout: 1000,
  maxTimeout: 5000
});

async-retry passes these options as the second argument.

await retry(async () => { /*...*/ }, {
  retries: 3,
  factor: 2,
  minTimeout: 1000,
  maxTimeout: 5000
});

p-retry uses a very similar options object.

await pRetry(async () => { /*...*/ }, {
  retries: 3,
  minTimeout: 1000,
  maxTimeout: 5000
});

promise-retry supports forever mode for infinite retries.

await promiseRetry((retry) => { /*...*/ }, {
  retries: 3,
  factor: 2,
  minTimeout: 1000,
  forever: false
});

retry-axios sets defaults on the Axios config object.

const client = axios.create({
  retryConfig: {
    retries: 3,
    retryDelay: 1000
  }
});

πŸ›‘ Error Handling: Aborting vs Continuing

Sometimes an error is permanent β€” like a 404 Not Found. Retrying won't help. You need a way to stop early.

retry requires you to check the error manually and call operation.retry(err).

operation.attempt(function(currentAttempt) {
  fetchData(function(err) {
    if (err && err.code === 'NOT_FOUND') {
      // Must manually finalize
      return operation.finalize(err);
    }
    if (operation.retry(err)) return;
  });
});

async-retry lets you call bail() to stop immediately.

await retry(async (bail) => {
  const err = await checkStatus();
  if (err && err.permanent) {
    bail(err); // Stops retries
    return;
  }
  throw new Error('Try again');
});

p-retry allows throwing specific error types to abort.

await pRetry(async () => {
  const err = await checkStatus();
  if (err && err.permanent) {
    throw new pRetry.AbortError(err); // Stops retries
  }
  throw new Error('Try again');
});

promise-retry relies on not calling the retry function.

await promiseRetry((retry) => {
  return fetchData().catch(err => {
    if (err.permanent) throw err; // Don't call retry()
    return retry(err);
  });
});

retry-axios uses a shouldRetry callback in config.

const client = axios.create({
  retryConfig: {
    shouldRetry: (err) => {
      if (err.response.status === 404) return false;
      return true;
    }
  }
});

πŸ”Œ Integration: Generic vs HTTP Specific

Some tools work for any code β€” database queries, file reads, or APIs. Others are built just for HTTP.

retry, async-retry, p-retry, and promise-retry are generic. They wrap any function.

// Works for DB, File, or Network
await pRetry(async () => {
  await db.query('SELECT * FROM users');
});

retry-axios only works with Axios. You cannot use it for database calls.

// Only works for Axios requests
await client.get('/users'); 
// Cannot wrap db.query() here

πŸ“Š Summary Table

Featureretryasync-retryp-retrypromise-retryretry-axios
StyleCallbackPromisePromisePromise + TriggerInterceptor
Async/Await❌ Noβœ… Yesβœ… Yesβœ… Yesβœ… Yes
Abort LogicManual Checkbail()AbortErrorSkip retry()shouldRetry
ScopeGenericGenericGenericGenericAxios Only
Best ForLegacy CodeServerlessGeneral AppCustom ControlAxios Projects

πŸ’‘ Final Recommendation

For most modern web applications, p-retry or async-retry are the best choices. They fit naturally into async/await code and are easy to read. If you are already using Axios for all your network calls, retry-axios saves you from wrapping every single request.

Avoid retry for new work β€” the callback style makes code harder to follow. Use promise-retry only if you need its unique ability to trigger retries from inside the function logic.

Pick the tool that matches your existing stack. Consistency matters more than minor feature differences.

How to Choose: async-retry vs p-retry vs promise-retry vs retry vs retry-axios

  • async-retry:

    Choose async-retry if you want a lightweight, Promise-based wrapper that is easy to read and works well in serverless environments. It is a solid choice for Next.js or Vercel projects where simplicity is key. It handles async functions cleanly without requiring extra boilerplate code.

  • p-retry:

    Choose p-retry if you are already using other utilities from the Sindre Sorhus ecosystem or need a robust, well-tested Promise library. It offers detailed error reporting through its onFailedAttempt callback. This package is ideal for general-purpose Node.js or frontend applications using async/await.

  • promise-retry:

    Choose promise-retry if you need fine-grained control over when a retry happens inside the operation itself. Unlike others that simply re-run the function, this passes a retry trigger into your code. It is often used in package managers and tools requiring specific network resilience patterns.

  • retry:

    Choose retry only if you are maintaining legacy code that relies on callbacks instead of Promises. For new projects, this library is generally not recommended because it forces you to write older-style code. It serves as the foundation for many other retry libraries but lacks modern async/await support.

  • retry-axios:

    Choose retry-axios if your project already depends heavily on Axios for HTTP requests and you want to add retry logic globally. It works as an interceptor, so you do not need to wrap individual calls. This is the most efficient option for Axios-specific workflows but cannot be used with other HTTP clients.

README for async-retry

async-retry

Retrying made simple, easy, and async.

Usage

// Packages
const retry = require('async-retry');
const fetch = require('node-fetch');

await retry(
  async (bail) => {
    // if anything throws, we retry
    const res = await fetch('https://google.com');

    if (403 === res.status) {
      // don't retry upon 403
      bail(new Error('Unauthorized'));
      return;
    }

    const data = await res.text();
    return data.substr(0, 500);
  },
  {
    retries: 5,
  }
);

API

retry(retrier : Function, opts : Object) => Promise
  • The supplied function can be async or not. In other words, it can be a function that returns a Promise or a value.
  • The supplied function receives two parameters
    1. A Function you can invoke to abort the retrying (bail)
    2. A Number identifying the attempt. The absolute first attempt (before any retries) is 1.
  • The opts are passed to node-retry. Read its docs
    • retries: The maximum amount of times to retry the operation. Default is 10.
    • factor: The exponential factor to use. Default is 2.
    • minTimeout: The number of milliseconds before starting the first retry. Default is 1000.
    • maxTimeout: The maximum number of milliseconds between two retries. Default is Infinity.
    • randomize: Randomizes the timeouts by multiplying with a factor between 1 to 2. Default is true.
    • onRetry: an optional Function that is invoked after a new retry is performed. It's passed the Error that triggered it as a parameter.

Authors