Last active
February 29, 2016 02:08
-
-
Save andresmatasuarez/f1a3251d9db4ff9d6fd6 to your computer and use it in GitHub Desktop.
Node Bluebird Exponential Backoff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Example usage | |
// The example will fetch current weather data from Buenos Aires, | |
// failing on purpose the first 2 times to demonstrate the Backoff strategy. | |
// Ensure you have Bluebird and superagent installed. | |
// | |
// Save the following code in example.js and run it with 'node example.js' | |
const Bluebird = require('bluebird'); | |
const superagent = require('superagent'); | |
const promiseUtils = require('./promise_utils'); | |
const WEATHER_URL = 'http://api.openweathermap.org/data/2.5/weather?q=Buenos%20Aires&appid=44db6a862fba0b067b1930da0d769e98'; | |
// Keep track of number of retries | |
var retries = 0; | |
promiseUtils.retry(() => { | |
console.log('Fetching data...'); | |
return Bluebird.try(() => { | |
if (retries < 2) { | |
retries += 1; | |
throw new Error(`Failed number ${retries}`); | |
} | |
}) | |
.then(() => Bluebird.promisify(superagent.get)(WEATHER_URL)) | |
.then((res) => res.body.weather[0].description); | |
}, { | |
maxAttempts: 2, | |
shouldRetry: (err) => { | |
console.log('Error:', err.message); | |
console.log('Retrying...'); | |
return true; | |
} | |
}) | |
.then((weather) => console.log('Finished. Buenos Aires weather is: ', weather)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* https://gist.github.com/andresmatasuarez/f1a3251d9db4ff9d6fd6 | |
* | |
* @module PromiseUtils | |
*/ | |
'use strict'; | |
const _ = require('lodash'); | |
const Bluebird = require('bluebird'); | |
const Chance = require('chance'); | |
const chance = new Chance(); | |
/** | |
* @default | |
*/ | |
const DEFAULT_SHOULD_RETRY = true; | |
/** | |
* @default | |
*/ | |
const DEFAULT_MAX_ATTEMPTS = 5; | |
/** | |
* @callback shouldRetry | |
* @param {object} err - Error object. | |
* @returns {boolean} true if err should cause the action to retry. false, otherwise. | |
*/ | |
/** | |
* retry | |
* | |
* @desc Returns a promise that gets resolved when 'action' is completed, or rejected if 'action' still fails after 'max' attempts using Exponential Backoff strategy. | |
* | |
* @param {function} action - Action to be performed. Must return a promise. | |
* @param {object} options - Configuration options | |
* @param {boolean|shouldRetry} [options.shouldRetry=DEFAULT_SHOULD_RETRY] - Condition that determines when a failing action should be retried or not. | |
* @param {number} [options.maxAttempts=DEFAULT_MAX_ATTEMPTS] - Max number of attempts before rejecting a failing action. | |
* @example | |
* const action = function(){ | |
* return asyncAction(...); | |
* }; | |
* | |
* retry(action).then(console.log); | |
* | |
* retry(action, { | |
* maxAttempts: 10, | |
* shouldRetry: (err) => { | |
* return err.code === 403; | |
* } | |
* }).then(console.log); | |
*/ | |
exports.retry = function retry(action, options){ | |
// Options | |
options = _.merge({ | |
shouldRetry: DEFAULT_SHOULD_RETRY, | |
maxAttempts: DEFAULT_MAX_ATTEMPTS | |
}, options); | |
if (!_.isFunction(options.shouldRetry)){ | |
options.shouldRetry = _.constant(!!options.shouldRetry); | |
} | |
function performAction(attempt, resolve, reject, err){ | |
return action() | |
.then(resolve) | |
.catch((err) => { | |
if (!options.shouldRetry(err) || attempt >= options.maxAttempts){ | |
return reject(err); | |
} | |
var delay = Math.pow(2, attempt) * 1000 + chance.millisecond(); | |
return Bluebird.delay(delay) | |
.then(() => performAction(attempt + 1, resolve, reject, err)); | |
}); | |
} | |
return new Bluebird(function(resolve, reject){ | |
performAction(0, resolve, reject); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment