Attempt

Greenkeeper badge Build Status Coverage Status npm version

This library exports a retry(...) function that can be used to invoke a function that returns a Promise multiple times until returned Promise is resolved or the max number of attempts is reached.

The delay between each attempt is configurable and allows multiple retry strategies.

The following features are supported:

Installation

Using NPM:

npm i @lifeomic/attempt

Using Yarn:

yarn add @lifeomic/attempt

Usage

Node.js / CommonJS:

const retry = require('@lifeomic/attempt').retry;

ES6 / TypeScript

import { retry } from '@lifeomic/attempt';
try {
  const result = await retry(async (context) => {
    // some code that returns a promise or resolved value
  }, options);
} catch (err) {
  // If the max number of attempts was exceeded then `err`
  // will be the last error that was thrown.
  //
  // If error is due to timeout then `err.code` will be the
  // string `ATTEMPT_TIMEOUT`.
}

The options argument is optional, and when absent the default values are assigned. All times/durations are in milliseconds.

The following object shows the default options:

{
  delay: 200,
  maxAttempts: 3,
  initialDelay: 0,
  minDelay: 0,
  maxDelay: 0,
  factor: 0,
  timeout: 0,
  jitter: false,
  initialJitter: false,
  handleError: null,
  handleTimeout: null,
  beforeAttempt: null,
  calculateDelay: null
}

NOTE:

If you are using a JavaScript runtime that doesn't support modern JavaScript features such as async/await then you will need to use a transpiler such as babel to transpile the JavaScript code to your target environment.

Supported options:

The context has the following properties:

Recipes

Retry with defaults

// Try the given operation up to 3 times with a delay of 200 between
// each attempt
const result = await retry(async function() {
  // do something that returns a promise
});

Stop retrying if an error indicates that we should not retry

// Try the given operation update to 4 times. The initial delay will be 0
// and subsequent delays will be 200, 400, 800
const result = await retry(async function() {
  // do something that returns a promise
}, {
  delay: 200,
  factor: 2,
  maxAttempts: 4,
  handleError (err, context) {
    if (err.retryable === false) {
      // We should abort because error indicates that request is not retryable
      context.abort();
    }
  }
});

Retry with exponential backoff

// Try the given operation update to 4 times. The initial delay will be 0
// and subsequent delays will be 200, 400, 800 (delay doubles each time due
// to factor of `2`)
const result = await retry(async function() {
  // do something that returns a promise
}, {
  delay: 200,
  factor: 2,
  maxAttempts: 4
});

Retry with exponential backoff and max delay

// Try the given operation up to 5 times. The initial delay will be 0
// and subsequent delays will be 200, 400, 500, 500 (capped at `maxDelay`)
const result = await retry(async function() {
  // do something that returns a promise
}, {
  delay: 200,
  factor: 2,
  maxAttempts: 5,
  maxDelay: 500
});

Retry with exponential backoff, jitter, min delay, and max delay

// Try the given operation 3 times. The initial delay will be 0
// and subsequent delays will be in the following range:
// - 100 to 200
// - 100 to 400
// - 100 to 500 (capped at `maxDelay`)
// - 100 to 500 (capped at `maxDelay`)
const result = await retry(async function() {
  // do something that returns a promise
}, {
  delay: 200,
  factor: 2,
  maxAttempts: 5,
  minDelay: 100,
  maxDelay: 500,
  jitter: true
});

Stop retrying if there is a timeout

// Try the given operation up to 5 times. The initial delay will be 0
// and subsequent delays will be 200, 400, 800, 1600.
//
// If an attempt fails to complete after 1 second then the retries
// are aborted and error with `code` `ATTEMPT_TIMEOUT` is thrown.
const result = await retry(async function() {
  // do something that returns a promise
}, {
  delay: 200,
  factor: 2,
  maxAttempts: 5,
  timeout: 1000
});

Stop retrying if there is a timeout but provide a fallback

// Try the given operation up to 5 times. The initial delay will be 0
// and subsequent delays will be 200, 400, 800, 1600.
//
// If an attempt fails to complete after 1 second then the retries
// are aborted and the `handleTimeout` implements some fallback logic.
const result = await retry(async function() {
  // do something that returns a promise
}, {
  delay: 200,
  factor: 2,
  maxAttempts: 5,
  timeout: 1000,
  async handleTimeout (context) {
    // do something that returns a promise or throw your own error
  }
});

Join libs.tech

...and unlock some superpowers

GitHub

We won't share your data with anyone else.