| function RetryOperation(timeouts, options) { |
| // Compatibility for the old (timeouts, retryForever) signature |
| if (typeof options === 'boolean') { |
| options = { forever: options }; |
| } |
| |
| this._timeouts = timeouts; |
| this._options = options || {}; |
| this._fn = null; |
| this._errors = []; |
| this._attempts = 1; |
| this._operationTimeout = null; |
| this._operationTimeoutCb = null; |
| this._timeout = null; |
| |
| if (this._options.forever) { |
| this._cachedTimeouts = this._timeouts.slice(0); |
| } |
| } |
| module.exports = RetryOperation; |
| |
| RetryOperation.prototype.retry = function(err) { |
| if (this._timeout) { |
| clearTimeout(this._timeout); |
| } |
| |
| if (!err) { |
| return false; |
| } |
| |
| this._errors.push(err); |
| |
| var timeout = this._timeouts.shift(); |
| if (timeout === undefined) { |
| if (this._cachedTimeouts) { |
| // retry forever, only keep last error |
| this._errors.splice(this._errors.length - 1, this._errors.length); |
| this._timeouts = this._cachedTimeouts.slice(0); |
| timeout = this._timeouts.shift(); |
| } else { |
| return false; |
| } |
| } |
| |
| var self = this; |
| var timer = setTimeout(function() { |
| self._attempts++; |
| |
| if (self._operationTimeoutCb) { |
| self._timeout = setTimeout(function() { |
| self._operationTimeoutCb(self._attempts); |
| }, self._operationTimeout); |
| |
| if (this._options.unref) { |
| self._timeout.unref(); |
| } |
| } |
| |
| self._fn(self._attempts); |
| }, timeout); |
| |
| if (this._options.unref) { |
| timer.unref(); |
| } |
| |
| return true; |
| }; |
| |
| RetryOperation.prototype.attempt = function(fn, timeoutOps) { |
| this._fn = fn; |
| |
| if (timeoutOps) { |
| if (timeoutOps.timeout) { |
| this._operationTimeout = timeoutOps.timeout; |
| } |
| if (timeoutOps.cb) { |
| this._operationTimeoutCb = timeoutOps.cb; |
| } |
| } |
| |
| var self = this; |
| if (this._operationTimeoutCb) { |
| this._timeout = setTimeout(function() { |
| self._operationTimeoutCb(); |
| }, self._operationTimeout); |
| } |
| |
| this._fn(this._attempts); |
| }; |
| |
| RetryOperation.prototype.try = function(fn) { |
| console.log('Using RetryOperation.try() is deprecated'); |
| this.attempt(fn); |
| }; |
| |
| RetryOperation.prototype.start = function(fn) { |
| console.log('Using RetryOperation.start() is deprecated'); |
| this.attempt(fn); |
| }; |
| |
| RetryOperation.prototype.start = RetryOperation.prototype.try; |
| |
| RetryOperation.prototype.errors = function() { |
| return this._errors; |
| }; |
| |
| RetryOperation.prototype.attempts = function() { |
| return this._attempts; |
| }; |
| |
| RetryOperation.prototype.mainError = function() { |
| if (this._errors.length === 0) { |
| return null; |
| } |
| |
| var counts = {}; |
| var mainError = null; |
| var mainErrorCount = 0; |
| |
| for (var i = 0; i < this._errors.length; i++) { |
| var error = this._errors[i]; |
| var message = error.message; |
| var count = (counts[message] || 0) + 1; |
| |
| counts[message] = count; |
| |
| if (count >= mainErrorCount) { |
| mainError = error; |
| mainErrorCount = count; |
| } |
| } |
| |
| return mainError; |
| }; |