1var RetryOperation = require('./retry_operation'); 2 3exports.operation = function(options) { 4 var timeouts = exports.timeouts(options); 5 return new RetryOperation(timeouts, { 6 forever: options && options.forever, 7 unref: options && options.unref, 8 maxRetryTime: options && options.maxRetryTime 9 }); 10}; 11 12exports.timeouts = function(options) { 13 if (options instanceof Array) { 14 return [].concat(options); 15 } 16 17 var opts = { 18 retries: 10, 19 factor: 2, 20 minTimeout: 1 * 1000, 21 maxTimeout: Infinity, 22 randomize: false 23 }; 24 for (var key in options) { 25 opts[key] = options[key]; 26 } 27 28 if (opts.minTimeout > opts.maxTimeout) { 29 throw new Error('minTimeout is greater than maxTimeout'); 30 } 31 32 var timeouts = []; 33 for (var i = 0; i < opts.retries; i++) { 34 timeouts.push(this.createTimeout(i, opts)); 35 } 36 37 if (options && options.forever && !timeouts.length) { 38 timeouts.push(this.createTimeout(i, opts)); 39 } 40 41 // sort the array numerically ascending 42 timeouts.sort(function(a,b) { 43 return a - b; 44 }); 45 46 return timeouts; 47}; 48 49exports.createTimeout = function(attempt, opts) { 50 var random = (opts.randomize) 51 ? (Math.random() + 1) 52 : 1; 53 54 var timeout = Math.round(random * opts.minTimeout * Math.pow(opts.factor, attempt)); 55 timeout = Math.min(timeout, opts.maxTimeout); 56 57 return timeout; 58}; 59 60exports.wrap = function(obj, options, methods) { 61 if (options instanceof Array) { 62 methods = options; 63 options = null; 64 } 65 66 if (!methods) { 67 methods = []; 68 for (var key in obj) { 69 if (typeof obj[key] === 'function') { 70 methods.push(key); 71 } 72 } 73 } 74 75 for (var i = 0; i < methods.length; i++) { 76 var method = methods[i]; 77 var original = obj[method]; 78 79 obj[method] = function retryWrapper(original) { 80 var op = exports.operation(options); 81 var args = Array.prototype.slice.call(arguments, 1); 82 var callback = args.pop(); 83 84 args.push(function(err) { 85 if (op.retry(err)) { 86 return; 87 } 88 if (err) { 89 arguments[0] = op.mainError(); 90 } 91 callback.apply(this, arguments); 92 }); 93 94 op.attempt(function() { 95 original.apply(obj, args); 96 }); 97 }.bind(obj, original); 98 obj[method].options = options; 99 } 100}; 101