diff --git a/README.md b/README.md index 6d54153..0ff33ea 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,29 @@ Allows you to stop the operation being retried. Useful for aborting the operatio #### retryOperation.reset() -Resets the internal state of the operation object, so that you can call `attempt()` again as if this was a new operation object. +Resets the internal state of the operation object. You can then call `retry()` (passing an error not required), and the entire operation will begin again as if it's the first attempt. + +``` +var operation = retry.operation(); +operation.attempt(function(currentAttempt) { + var err; + try{ + doSomething(); + } + catch(e){ + e = err; + } + + if (needToStartFromScratch()){ + operation.reset(); + operation.retry(); + return; + } + + if (operation.retry(err)) return; +}); +``` + #### retryOperation.attempts() diff --git a/lib/retry_operation.js b/lib/retry_operation.js index 1e56469..1740bdf 100644 --- a/lib/retry_operation.js +++ b/lib/retry_operation.js @@ -15,6 +15,7 @@ function RetryOperation(timeouts, options) { this._operationTimeoutCb = null; this._timeout = null; this._operationStart = null; + this._reset = false; if (this._options.forever) { this._cachedTimeouts = this._timeouts.slice(0); @@ -23,8 +24,9 @@ function RetryOperation(timeouts, options) { module.exports = RetryOperation; RetryOperation.prototype.reset = function() { - this._attempts = 1; - this._timeouts = this._originalTimeouts; + this._attempts = 0; + this._timeouts = JSON.parse(JSON.stringify(this._originalTimeouts)); + this._reset = true; } RetryOperation.prototype.stop = function() { @@ -36,32 +38,37 @@ RetryOperation.prototype.stop = function() { this._cachedTimeouts = null; }; -RetryOperation.prototype.retry = function(err) { +RetryOperation.prototype.retry = function(err, force) { if (this._timeout) { clearTimeout(this._timeout); } - - if (!err) { - return false; - } - var currentTime = new Date().getTime(); - if (err && currentTime - this._operationStart >= this._maxRetryTime) { - this._errors.unshift(new Error('RetryOperation timeout occurred')); - return false; + + if (this._reset){ + this._reset = 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 { + else{ + if (!err) { return false; } + var currentTime = new Date().getTime(); + if (err && currentTime - this._operationStart >= this._maxRetryTime) { + this._errors.unshift(new Error('RetryOperation timeout occurred')); + 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; diff --git a/test/integration/test-retry-operation.js b/test/integration/test-retry-operation.js index e351bb6..a057cbc 100644 --- a/test/integration/test-retry-operation.js +++ b/test/integration/test-retry-operation.js @@ -7,6 +7,7 @@ var retry = require(common.dir.lib + '/retry'); var error = new Error('some error'); var operation = retry.operation([1, 2, 3]); var attempts = 0; + var allAttempts = 0; var finalCallback = fake.callback('finalCallback'); fake.expectAnytime(finalCallback); @@ -14,32 +15,31 @@ var retry = require(common.dir.lib + '/retry'); var expectedFinishes = 1; var finishes = 0; - var fn = function() { - operation.attempt(function(currentAttempt) { - attempts++; - assert.equal(currentAttempt, attempts); - if (operation.retry(error)) { - return; - } - - finishes++ - assert.equal(expectedFinishes, finishes); - assert.strictEqual(attempts, 4); - assert.strictEqual(operation.attempts(), attempts); - assert.strictEqual(operation.mainError(), error); - - if (finishes < 2) { - attempts = 0; - expectedFinishes++; - operation.reset(); - fn() - } else { - finalCallback(); - } - }); - }; + operation.attempt(function(currentAttempt) { + attempts++; + allAttempts++; + assert.equal(currentAttempt, attempts); + + // perform a single reset after 2 attempts + if (attempts === 2 && allAttempts === 2) { + attempts = 0; + operation.reset(); + operation.retry(); + return; + } + + if (operation.retry(error)) { + return; + } + + finishes++ + assert.equal(expectedFinishes, finishes); + assert.strictEqual(allAttempts, 6); + assert.strictEqual(operation.attempts(), attempts); + assert.strictEqual(operation.mainError(), error); + finalCallback(); + }); - fn(); })(); (function testErrors() {