diff --git a/README.md b/README.md index 976f688..277aae0 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,23 @@ Returns `false` when no `error` value is given, or the maximum amount of retries has been reached. Otherwise it returns `true`, and retries the operation after the timeout for -the current attempt number. +the current attempt number. `retryOperation.nextTimeout()` tells you how long +that timeout is. + +#### retryOperation.nextTimeout() + +Returns the number of milliseconds the operation waits before the retry that the +last `retryOperation.retry()` call scheduled. Returns `null` if that call did not +schedule a retry, or if no retry has been scheduled since the operation was +created, `reset()` or `stop()`ped. Call it right after `retry()`: inside the next +attempt it still returns the timeout that led up to that attempt. + +```javascript +if (operation.retry(err)) { + console.warn('Request failed, retrying in ' + operation.nextTimeout() + 'ms'); + return; +} +``` #### retryOperation.stop() diff --git a/lib/retry_operation.js b/lib/retry_operation.js index 105ce72..5f2b2f1 100644 --- a/lib/retry_operation.js +++ b/lib/retry_operation.js @@ -16,6 +16,7 @@ function RetryOperation(timeouts, options) { this._timeout = null; this._operationStart = null; this._timer = null; + this._nextTimeout = null; if (this._options.forever) { this._cachedTimeouts = this._timeouts.slice(0); @@ -26,6 +27,7 @@ module.exports = RetryOperation; RetryOperation.prototype.reset = function() { this._attempts = 1; this._timeouts = this._originalTimeouts.slice(0); + this._nextTimeout = null; } RetryOperation.prototype.stop = function() { @@ -38,6 +40,7 @@ RetryOperation.prototype.stop = function() { this._timeouts = []; this._cachedTimeouts = null; + this._nextTimeout = null; }; RetryOperation.prototype.retry = function(err) { @@ -45,6 +48,8 @@ RetryOperation.prototype.retry = function(err) { clearTimeout(this._timeout); } + this._nextTimeout = null; + if (!err) { return false; } @@ -62,12 +67,14 @@ RetryOperation.prototype.retry = function(err) { if (this._cachedTimeouts) { // retry forever, only keep last error this._errors.splice(0, this._errors.length - 1); - timeout = this._cachedTimeouts.slice(-1); + timeout = this._cachedTimeouts[this._cachedTimeouts.length - 1]; } else { return false; } } + this._nextTimeout = timeout; + var self = this; this._timer = setTimeout(function() { self._attempts++; @@ -136,6 +143,10 @@ RetryOperation.prototype.attempts = function() { return this._attempts; }; +RetryOperation.prototype.nextTimeout = function() { + return this._nextTimeout; +}; + RetryOperation.prototype.mainError = function() { if (this._errors.length === 0) { return null; diff --git a/test/integration/test-next-timeout.js b/test/integration/test-next-timeout.js new file mode 100644 index 0000000..1304c8b --- /dev/null +++ b/test/integration/test-next-timeout.js @@ -0,0 +1,36 @@ +var common = require('../common'); +var assert = common.assert; +var retry = require(common.dir.lib + '/retry'); + +(function testNextTimeout() { + var operation = retry.operation([1, 2]); + var nextTimeouts = []; + assert.strictEqual(operation.nextTimeout(), null); + + operation.attempt(function() { + operation.retry(new Error('foo')); + nextTimeouts.push(operation.nextTimeout()); + }); + + process.on('exit', function() { + assert.deepStrictEqual(nextTimeouts, [1, 2, null]); + }); +})(); + +(function testNextTimeoutWhenRetryingForever() { + var operation = retry.operation({retries: 1, minTimeout: 1, forever: true}); + var nextTimeouts = []; + + operation.attempt(function(attempt) { + operation.retry(new Error('foo')); + nextTimeouts.push(operation.nextTimeout()); + if (attempt === 3) { + operation.stop(); + nextTimeouts.push(operation.nextTimeout()); + } + }); + + process.on('exit', function() { + assert.deepStrictEqual(nextTimeouts, [1, 1, 1, null]); + }); +})();