diff --git a/README.md b/README.md index 976f688..4146d65 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ milliseconds. If `options` is an array, a copy of that array is returned. * `minTimeout`: The number of milliseconds before starting the first retry. Default is `1000`. * `maxTimeout`: The maximum number of milliseconds between two retries. Default is `Infinity`. * `randomize`: Randomizes the timeouts by multiplying with a factor between `1` to `2`. Default is `false`. +* `forever`: Whether the timeouts are for an operation that retries forever. If so and `retries` is `0`, one timeout is still returned, so there is a delay to repeat. Default is `false`. The formula used to calculate the individual timeouts is: @@ -186,7 +187,7 @@ Available options: If `forever` is true, the following changes happen: * `RetryOperation.errors()` will only output an array of one item: the last error. -* `RetryOperation` will repeatedly use the `timeouts` array. Once all of its timeouts have been used up, it restarts with the first timeout, then uses the second and so on. +* `RetryOperation` will keep retrying with the last timeout once all of the `timeouts` have been used up. #### retryOperation.errors() @@ -211,7 +212,7 @@ Defines the function `fn` that is to be retried and executes it for the first time right away. The `fn` function can receive an optional `currentAttempt` callback that represents the number of attempts to execute `fn` so far. Optionally defines `timeoutOps` which is an object having a property `timeout` in miliseconds and a property `cb` callback function. -Whenever your retry operation takes longer than `timeout` to execute, the timeout callback function `cb` is called. +Whenever your retry operation takes longer than `timeout` to execute, the timeout callback function `cb` is called with the current attempt number. #### retryOperation.try(fn) diff --git a/lib/retry_operation.js b/lib/retry_operation.js index 105ce72..f8c7a0f 100644 --- a/lib/retry_operation.js +++ b/lib/retry_operation.js @@ -62,7 +62,7 @@ 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; } @@ -107,8 +107,12 @@ RetryOperation.prototype.attempt = function(fn, timeoutOps) { var self = this; if (this._operationTimeoutCb) { this._timeout = setTimeout(function() { - self._operationTimeoutCb(); + self._operationTimeoutCb(self._attempts); }, self._operationTimeout); + + if (this._options.unref) { + this._timeout.unref(); + } } this._operationStart = new Date().getTime(); diff --git a/test/integration/test-forever-timeout.js b/test/integration/test-forever-timeout.js new file mode 100644 index 0000000..3af083f --- /dev/null +++ b/test/integration/test-forever-timeout.js @@ -0,0 +1,21 @@ +var common = require('../common'); +var assert = common.assert; +var RetryOperation = require(common.dir.lib + '/retry_operation'); + +(function testForeverRepeatsLastTimeout() { + var operation = new RetryOperation([1, 2], {forever: true}); + var originalSetTimeout = global.setTimeout; + var delays = []; + global.setTimeout = function(fn, delay) { + delays.push(delay); + return originalSetTimeout(function() {}, 0); + }; + + operation.attempt(function() {}); + for (var i = 0; i < 4; i++) { + operation.retry(new Error('foo')); + } + global.setTimeout = originalSetTimeout; + + assert.deepStrictEqual(delays, [1, 2, 2, 2]); +})(); diff --git a/test/integration/test-operation-timeout.js b/test/integration/test-operation-timeout.js new file mode 100644 index 0000000..0978d98 --- /dev/null +++ b/test/integration/test-operation-timeout.js @@ -0,0 +1,32 @@ +var common = require('../common'); +var assert = common.assert; +var retry = require(common.dir.lib + '/retry'); + +(function testOperationTimeoutReceivesAttempts() { + var operation = retry.operation([1]); + var timedOutAttempts = []; + + operation.attempt(function() {}, { + timeout: 1, + cb: function(attempts) { + timedOutAttempts.push(attempts); + operation.retry(new Error('timeout')); + } + }); + + process.on('exit', function() { + assert.deepStrictEqual(timedOutAttempts, [1, 2]); + }); +})(); + +(function testOperationTimeoutIsUnrefed() { + var operation = retry.operation({unref: true}); + + operation.attempt(function() {}, { + timeout: 60 * 1000, + cb: function() {} + }); + + assert.strictEqual(operation._timeout.hasRef(), false); + operation.stop(); +})();