| var RetryOperation = require('./retry_operation'); |
| |
| exports.operation = function(options) { |
| var timeouts = exports.timeouts(options); |
| return new RetryOperation(timeouts, { |
| forever: options && options.forever, |
| unref: options && options.unref, |
| maxRetryTime: options && options.maxRetryTime |
| }); |
| }; |
| |
| exports.timeouts = function(options) { |
| if (options instanceof Array) { |
| return [].concat(options); |
| } |
| |
| var opts = { |
| retries: 10, |
| factor: 2, |
| minTimeout: 1 * 1000, |
| maxTimeout: Infinity, |
| randomize: false |
| }; |
| for (var key in options) { |
| opts[key] = options[key]; |
| } |
| |
| if (opts.minTimeout > opts.maxTimeout) { |
| throw new Error('minTimeout is greater than maxTimeout'); |
| } |
| |
| var timeouts = []; |
| for (var i = 0; i < opts.retries; i++) { |
| timeouts.push(this.createTimeout(i, opts)); |
| } |
| |
| if (options && options.forever && !timeouts.length) { |
| timeouts.push(this.createTimeout(i, opts)); |
| } |
| |
| // sort the array numerically ascending |
| timeouts.sort(function(a,b) { |
| return a - b; |
| }); |
| |
| return timeouts; |
| }; |
| |
| exports.createTimeout = function(attempt, opts) { |
| var random = (opts.randomize) |
| ? (Math.random() + 1) |
| : 1; |
| |
| var timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempt)); |
| timeout = Math.min(timeout, opts.maxTimeout); |
| |
| return timeout; |
| }; |
| |
| exports.wrap = function(obj, options, methods) { |
| if (options instanceof Array) { |
| methods = options; |
| options = null; |
| } |
| |
| if (!methods) { |
| methods = []; |
| for (var key in obj) { |
| if (typeof obj[key] === 'function') { |
| methods.push(key); |
| } |
| } |
| } |
| |
| for (var i = 0; i < methods.length; i++) { |
| var method = methods[i]; |
| var original = obj[method]; |
| |
| obj[method] = function retryWrapper(original) { |
| var op = exports.operation(options); |
| var args = Array.prototype.slice.call(arguments, 1); |
| var callback = args.pop(); |
| |
| args.push(function(err) { |
| if (op.retry(err)) { |
| return; |
| } |
| if (err) { |
| arguments[0] = op.mainError(); |
| } |
| callback.apply(this, arguments); |
| }); |
| |
| op.attempt(function() { |
| original.apply(obj, args); |
| }); |
| }.bind(obj, original); |
| obj[method].options = options; |
| } |
| }; |