Comparing version 1.1.0 to 1.2.0
23
index.js
@@ -7,2 +7,3 @@ /* | ||
var Backoff = require('./lib/backoff'), | ||
FunctionCall = require('./lib/function_call.js'), | ||
FibonacciBackoffStrategy = require('./lib/strategy/fibonacci'), | ||
@@ -12,2 +13,3 @@ ExponentialBackoffStrategy = require('./lib/strategy/exponential'); | ||
module.exports.Backoff = Backoff; | ||
module.exports.FunctionCall = FunctionCall; | ||
module.exports.FibonacciStrategy = FibonacciBackoffStrategy; | ||
@@ -19,2 +21,3 @@ module.exports.ExponentialStrategy = ExponentialBackoffStrategy; | ||
* @param options Fibonacci backoff strategy arguments. | ||
* @return The fibonacci backoff. | ||
* @see FibonacciBackoffStrategy | ||
@@ -29,2 +32,3 @@ */ | ||
* @param options Exponential strategy arguments. | ||
* @return The exponential backoff. | ||
* @see ExponentialBackoffStrategy | ||
@@ -36,1 +40,20 @@ */ | ||
/** | ||
* Calls a function in a backoff loop. | ||
* @param fn Function to wrap in a backoff handler. | ||
* @param vargs Function's arguments (var args). | ||
* @param callback Function's callback. | ||
* @return The call handle. | ||
*/ | ||
module.exports.call = function(fn, vargs, callback) { | ||
var args = Array.prototype.slice.call(arguments); | ||
var call = new FunctionCall(args[0], args.slice(1, args.length - 1), | ||
args[args.length - 1]); | ||
process.nextTick(function() { | ||
call.call(); | ||
}); | ||
return call; | ||
}; | ||
{ | ||
"name": "backoff", | ||
"description": "Fibonacci and exponential backoffs.", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"author": "Mathieu Turcotte <turcotte.mat@gmail.com>", | ||
@@ -14,3 +14,3 @@ "keywords": ["backoff", "fibonacci", "exponential"], | ||
"nodeunit": "0.7", | ||
"jshint": "0.9" | ||
"jshint": "0.9.0" | ||
}, | ||
@@ -17,0 +17,0 @@ "scripts": { |
130
README.md
@@ -19,2 +19,4 @@ # Backoff for Node.js [![Build Status](https://secure.travis-ci.org/MathieuTurcotte/node-backoff.png?branch=master)](http://travis-ci.org/MathieuTurcotte/node-backoff) | ||
### Object Oriented | ||
The usual way to instantiate a new `Backoff` object is to use one predefined | ||
@@ -47,3 +49,5 @@ factory method: `backoff.fibonacci([options])`, `backoff.exponential([options])`. | ||
// Do something when backoff ends, e.g. retry a failed | ||
// operation (DNS lookup, API call, etc.). | ||
// operation (DNS lookup, API call, etc.). If it fails | ||
// again then backoff, otherwise reset the backoff | ||
// instance. | ||
fibonacciBackoff.backoff(); | ||
@@ -61,3 +65,3 @@ }); | ||
The previous example would print: | ||
The previous example would print the following. | ||
@@ -81,2 +85,24 @@ ``` | ||
### Functional | ||
It's also possible to avoid some boilerplate code when invoking an asynchronous | ||
function in a backoff loop by using `backoff.call(fn, args)`. | ||
Typical usage looks like the following. | ||
``` js | ||
var call = backoff.call(get, 'https://duplika.ca/', function(err, res) { | ||
console.log('Retries: ' + call.getResults().length); | ||
if (err) { | ||
console.log('Error: ' + err.message); | ||
} else { | ||
console.log('Status: ' + res.statusCode); | ||
} | ||
}); | ||
call.setStrategy(new backoff.ExponentialStrategy()); | ||
call.failAfter(10); | ||
``` | ||
## API | ||
@@ -88,3 +114,3 @@ | ||
See bellow for the options description. | ||
See bellow for options description. | ||
@@ -95,3 +121,3 @@ ### backoff.exponential([options]) | ||
The options are: | ||
The options are the following. | ||
@@ -106,2 +132,17 @@ - randomisationFactor: defaults to 0, must be between 0 and 1 | ||
### backoff.call(fn, [args, ...], callback) | ||
- fn: function to call in a backoff handler | ||
- args: function's arguments | ||
- callback: function's callback accepting an error as its first argument | ||
Calls an asynchronous function in a backoff handler so that it gets | ||
automatically retried on error. The wrapped function will get retried until it | ||
succeds or reach the maximum number of backoffs. In both cases, the callback | ||
function will be invoked with the last results returned by the wrapped | ||
function. | ||
This function returns a `FunctionCall` instance that is going to be invoked on | ||
next tick and can be used to configure and/or abort the call. | ||
### Class Backoff | ||
@@ -185,3 +226,3 @@ | ||
The options are: | ||
The options are the following. | ||
@@ -198,3 +239,3 @@ - randomisationFactor: defaults to 0, must be between 0 and 1 | ||
The options are: | ||
The options are the following. | ||
@@ -205,4 +246,81 @@ - randomisationFactor: defaults to 0, must be between 0 and 1 | ||
### Class FunctionCall | ||
This class manages the calling of an asynchronous function within a backoff | ||
loop. | ||
This class should rarely be instantiated directly since the factory method | ||
`backoff.call(fn, [args, ...], callback)` offers a more convenient and safer | ||
way to create `FunctionCall` instances. | ||
#### new FunctionCall(fn, args, callback) | ||
- fn: asynchronous function to call | ||
- args: an array containing fn's args | ||
- callback: fn's callback | ||
Constructs a function handler for the given asynchronous function. | ||
#### call.setStrategy(strategy) | ||
- strategy: strategy instance to use, defaults to `FibonacciStrategy`. | ||
Sets the backoff strategy to use. This method should be called before | ||
`call.call()`. | ||
#### call.failAfter(maxNumberOfBackoffs) | ||
- maxNumberOfBackoffs: maximum number of backoffs before the call is aborted | ||
Sets the maximum number of backoffs before the call is aborted. This method | ||
should be called before `call.call()`. | ||
#### call.getResults() | ||
Retrieves all intermediary results returned by the wrapped function. This | ||
method can be called at any point in time during the call life cycle, i.e. | ||
before, during and after the wrapped function invocation. | ||
Returns an array of arrays containing the results returned by the wrapped | ||
function for each call. For example, to get the error code returned by the | ||
second call, one would do the following. | ||
``` js | ||
var results = call.getResults(); | ||
var error = results[1][0]; | ||
``` | ||
#### call.call() | ||
Calls the wrapped function. This method should only be called once. | ||
#### call.abort() | ||
Aborts the call. | ||
Past results can be retrieved using `call.getResults()`. This method can be | ||
called at any point in time during the call life cycle, i.e. before, during | ||
and after the wrapped function invocation. | ||
#### Event: 'call' | ||
- args: wrapped function's arguments | ||
Emitted each time the wrapped function is called. | ||
#### Event: 'callback' | ||
- results: wrapped function's return values | ||
Emitted each time the wrapped function invoke its callback. | ||
#### Event: 'backoff' | ||
- number: backoff number, starts at 0 | ||
- delay: backoff delay in milliseconds | ||
Emitted each time a backoff operation is started. | ||
## License | ||
This code is free to use under the terms of the [MIT license](http://mturcotte.mit-license.org/). |
@@ -32,3 +32,4 @@ /* | ||
test.ok(spy.calledOnce); | ||
test.ok(spy.calledOnce, | ||
'Backoff event should be emitted when backoff starts.'); | ||
test.done(); | ||
@@ -46,3 +47,4 @@ }, | ||
test.ok(spy.calledOnce); | ||
test.ok(spy.calledOnce, | ||
'Ready event should be emitted when backoff ends.'); | ||
test.done(); | ||
@@ -59,3 +61,4 @@ }, | ||
test.equal(spy.getCall(0).args[1], 989); | ||
test.equal(spy.getCall(0).args[1], 989, 'Backoff event should ' + | ||
'carry the backoff delay as its second argument.'); | ||
test.done(); | ||
@@ -73,3 +76,4 @@ }, | ||
test.equal(spy.getCall(0).args[1], 989); | ||
test.equal(spy.getCall(0).args[1], 989, 'Ready event should ' + | ||
'carry the backoff delay as its second argument.'); | ||
test.done(); | ||
@@ -92,5 +96,5 @@ }, | ||
// Failure should occur on the third call, and not before. | ||
test.ok(!spy.calledOnce); | ||
test.ok(!spy.calledOnce, 'Fail event shouldn\'t have been emitted.'); | ||
this.backoff.backoff(); | ||
test.ok(spy.calledOnce); | ||
test.ok(spy.calledOnce, 'Fail event should have been emitted.'); | ||
@@ -108,3 +112,3 @@ test.done(); | ||
backoff.backoff(); | ||
}); | ||
}, /in progress/); | ||
@@ -118,3 +122,3 @@ test.done(); | ||
backoff.failAfter(0); | ||
}); | ||
}, /must be greater than 0/); | ||
test.done(); | ||
@@ -134,3 +138,3 @@ }, | ||
test.equals(spy.callCount, 0); | ||
test.equals(spy.callCount, 0, 'Reset should have aborted the backoff.'); | ||
test.done(); | ||
@@ -141,3 +145,4 @@ }, | ||
this.backoff.reset(); | ||
test.ok(this.backoffStrategy.reset.calledOnce); | ||
test.ok(this.backoffStrategy.reset.calledOnce, | ||
'The backoff strategy should have been resetted.'); | ||
test.done(); | ||
@@ -155,3 +160,4 @@ }, | ||
test.ok(this.backoffStrategy.reset.calledOnce); | ||
test.ok(this.backoffStrategy.reset.calledOnce, | ||
'Backoff should have been resetted after failure.'); | ||
test.done(); | ||
@@ -162,16 +168,18 @@ }, | ||
this.backoffStrategy.next.returns(10); | ||
var expectedNumbers = [0, 1, 2, 3, 4], | ||
actualNumbers = []; | ||
var spy = new sinon.spy(); | ||
this.backoff.on('backoff', spy); | ||
for (var i = 0; i < 10; i++) { | ||
for (var i = 0; i < expectedNumbers.length; i++) { | ||
this.backoff.backoff(); | ||
this.clock.tick(10); | ||
actualNumbers.push(spy.getCall(i).args[0]); | ||
} | ||
for (var j = 0; j < 10; j++) { | ||
test.equals(spy.getCall(j).args[0], j); | ||
} | ||
test.deepEqual(expectedNumbers, actualNumbers, | ||
'Backoff number should increase from 0 to N - 1.'); | ||
test.done(); | ||
} | ||
}; |
@@ -21,9 +21,11 @@ /* | ||
// Exponential sequence: x[i] = x[i-1] * 2. | ||
var delays = [10, 20, 40, 80, 160, 320, 640, 1000, 1000]; | ||
var expectedDelays = [10, 20, 40, 80, 160, 320, 640, 1000, 1000]; | ||
var actualDelays = []; | ||
delays.forEach(function(delay) { | ||
var backoff = this.strategy.next(); | ||
test.equals(backoff, delay); | ||
}, this); | ||
for (var i = 0; i < expectedDelays.length; i++) { | ||
actualDelays.push(this.strategy.next()); | ||
} | ||
test.deepEqual(expectedDelays, actualDelays, | ||
'Generated delays should follow an exponential sequence.'); | ||
test.done(); | ||
@@ -42,5 +44,6 @@ }, | ||
var backoffDelay = strategy.next(); | ||
test.equals(backoffDelay, 10); | ||
test.equals(backoffDelay, 10, | ||
'Strategy should return the initial delay after reset.'); | ||
test.done(); | ||
} | ||
}; |
@@ -21,9 +21,11 @@ /* | ||
// Fibonacci sequence: x[i] = x[i-1] + x[i-2]. | ||
var delays = [10, 10, 20, 30, 50, 80, 130, 210, 340, 550, 890, 1000]; | ||
var expectedDelays = [10, 10, 20, 30, 50, 80, 130, 210, 340, 550, 890, 1000]; | ||
var actualDelays = []; | ||
delays.forEach(function(delay) { | ||
var backoff = this.strategy.next(); | ||
test.equals(backoff, delay); | ||
}, this); | ||
for (var i = 0; i < expectedDelays.length; i++) { | ||
actualDelays.push(this.strategy.next()); | ||
} | ||
test.deepEqual(expectedDelays, actualDelays, | ||
'Generated delays should follow a Fibonacci sequence.'); | ||
test.done(); | ||
@@ -42,5 +44,6 @@ }, | ||
var backoffDelay = strategy.next(); | ||
test.equals(backoffDelay, 10); | ||
test.equals(backoffDelay, 10, | ||
'Strategy should return the initial delay after reset.'); | ||
test.done(); | ||
} | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
53208
26
1088
317