Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

backoff

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

backoff - npm Package Compare versions

Comparing version 2.3.0 to 2.4.0

docs/backoff.html

8

CHANGES.md
# Changelog
## 2.4.0
- Replace `FunctionCall.getResults` by `FunctionCall.getLastResult` to avoid
storing intermediary results forever as this may lead to memory exhaustion
when used in conjunction with an infinite number of backoffs.
- Add `FunctionCall.getNumRetries` which returns the number of times the
wrapped function was retried.
## 2.3.0

@@ -4,0 +12,0 @@

2

examples/function_call.js

@@ -30,3 +30,3 @@ #!/usr/bin/env node

// Notice how the call is captured inside the closure.
console.log('Retries: ' + call.getResults().length);
console.log('Num retries: ' + call.getNumRetries());

@@ -33,0 +33,0 @@ if (err) {

@@ -1,5 +0,3 @@

/*
* Copyright (c) 2012 Mathieu Turcotte
* Licensed under the MIT license.
*/
// Copyright (c) 2012 Mathieu Turcotte
// Licensed under the MIT license.

@@ -16,8 +14,3 @@ var Backoff = require('./lib/backoff');

/**
* Constructs a Fibonacci backoff.
* @param options Fibonacci backoff strategy arguments.
* @return The fibonacci backoff.
* @see FibonacciBackoffStrategy
*/
// Constructs a Fibonacci backoff.
module.exports.fibonacci = function(options) {

@@ -27,8 +20,3 @@ return new Backoff(new FibonacciBackoffStrategy(options));

/**
* Constructs an exponential backoff.
* @param options Exponential strategy arguments.
* @return The exponential backoff.
* @see ExponentialBackoffStrategy
*/
// Constructs an exponential backoff.
module.exports.exponential = function(options) {

@@ -38,9 +26,3 @@ return new Backoff(new ExponentialBackoffStrategy(options));

/**
* Constructs a FunctionCall for the given function and arguments.
* @param fn The function to wrap in a backoff handler.
* @param vargs The function's arguments (var args).
* @param callback The function's callback.
* @return The FunctionCall instance.
*/
// Constructs a FunctionCall for the given function and arguments.
module.exports.call = function(fn, vargs, callback) {

@@ -47,0 +29,0 @@ var args = Array.prototype.slice.call(arguments);

@@ -1,14 +0,10 @@

/*
* Copyright (c) 2012 Mathieu Turcotte
* Licensed under the MIT license.
*/
// Copyright (c) 2012 Mathieu Turcotte
// Licensed under the MIT license.
var events = require('events');
var precond = require('precond');
var util = require('util');
/**
* Backoff driver.
* @param backoffStrategy Backoff delay generator/strategy.
* @constructor
*/
// A class to hold the state of a backoff operation. Accepts a backoff strategy
// to generate the backoff delays.
function Backoff(backoffStrategy) {

@@ -29,12 +25,8 @@ events.EventEmitter.call(this);

/**
* Sets a limit, greater than 0, on the maximum number of backoffs. A 'fail'
* event will be emitted when the limit is reached.
* @param maxNumberOfRetry The maximum number of backoffs.
*/
// Sets a limit, greater than 0, on the maximum number of backoffs. A 'fail'
// event will be emitted when the limit is reached.
Backoff.prototype.failAfter = function(maxNumberOfRetry) {
if (maxNumberOfRetry < 1) {
throw new Error('Maximum number of retry must be greater than 0. ' +
'Actual: ' + maxNumberOfRetry);
}
precond.checkArgument(maxNumberOfRetry > 0,
'Expected a maximum number of retry greater than 0 but got %s.',
maxNumberOfRetry);

@@ -44,11 +36,6 @@ this.maxNumberOfRetry_ = maxNumberOfRetry;

/**
* Starts a backoff operation.
* @param err Optional paramater to let the listeners know why the backoff
* operation was started.
*/
// Starts a backoff operation. Accepts an optional parameter to let the
// listeners know why the backoff operation was started.
Backoff.prototype.backoff = function(err) {
if (this.timeoutID_ !== -1) {
throw new Error('Backoff in progress.');
}
precond.checkState(this.timeoutID_ === -1, 'Backoff in progress.');

@@ -65,6 +52,3 @@ if (this.backoffNumber_ === this.maxNumberOfRetry_) {

/**
* Handles the backoff timeout completion.
* @private
*/
// Handles the backoff timeout completion.
Backoff.prototype.onBackoff_ = function() {

@@ -76,6 +60,3 @@ this.timeoutID_ = -1;

/**
* Stops any backoff operation and resets the backoff delay to its inital
* value.
*/
// Stops any backoff operation and resets the backoff delay to its inital value.
Backoff.prototype.reset = function() {

@@ -82,0 +63,0 @@ this.backoffNumber_ = 0;

@@ -1,7 +0,6 @@

/*
* Copyright (c) 2012 Mathieu Turcotte
* Licensed under the MIT license.
*/
// Copyright (c) 2012 Mathieu Turcotte
// Licensed under the MIT license.
var events = require('events');
var precond = require('precond');
var util = require('util');

@@ -12,35 +11,15 @@

/**
* Returns true if the specified value is a function
* @param val Variable to test.
* @return Whether variable is a function.
*/
function isFunction(val) {
return typeof val == 'function';
}
/**
* Manages the calling of a function in a backoff loop.
* @param fn Function to wrap in a backoff handler.
* @param args Array of function's arguments.
* @param callback Function's callback.
* @constructor
*/
// Wraps a function to be called in a backoff loop.
function FunctionCall(fn, args, callback) {
events.EventEmitter.call(this);
if (!isFunction(fn)) {
throw new Error('fn should be a function.' +
'Actual: ' + typeof fn);
}
precond.checkIsFunction(fn, 'Expected fn to be a function.');
precond.checkIsArray(args, 'Expected args to be an array.');
precond.checkIsFunction(callback, 'Expected callback to be a function.');
if (!isFunction(callback)) {
throw new Error('callback should be a function.' +
'Actual: ' + typeof fn);
}
this.function_ = fn;
this.arguments_ = args;
this.callback_ = callback;
this.results_ = [];
this.lastResult_ = [];
this.numRetries_ = 0;

@@ -55,16 +34,16 @@ this.backoff_ = null;

/**
* Enum of states in which the FunctionCall can be.
* @private
*/
// States in which the call can be.
FunctionCall.State_ = {
// Call isn't started yet.
PENDING: 0,
// Call is in progress.
RUNNING: 1,
// Call completed successfully which means that either the wrapped function
// returned successfully or the maximal number of backoffs was reached.
COMPLETED: 2,
// The call was aborted.
ABORTED: 3
};
/**
* @return Whether the call is pending.
*/
// Checks whether the call is pending.
FunctionCall.prototype.isPending = function() {

@@ -74,5 +53,3 @@ return this.state_ == FunctionCall.State_.PENDING;

/**
* @return Whether the call is in progress.
*/
// Checks whether the call is in progress.
FunctionCall.prototype.isRunning = function() {

@@ -82,5 +59,3 @@ return this.state_ == FunctionCall.State_.RUNNING;

/**
* @return Whether the call is completed.
*/
// Checks whether the call is completed.
FunctionCall.prototype.isCompleted = function() {

@@ -90,5 +65,3 @@ return this.state_ == FunctionCall.State_.COMPLETED;

/**
* @return Whether the call is aborted.
*/
// Checks whether the call is aborted.
FunctionCall.prototype.isAborted = function() {

@@ -98,44 +71,31 @@ return this.state_ == FunctionCall.State_.ABORTED;

/**
* Sets the backoff strategy.
* @param strategy The backoff strategy to use.
* @return Itself for chaining.
*/
// Sets the backoff strategy to use. Can only be called before the call is
// started otherwise an exception will be thrown.
FunctionCall.prototype.setStrategy = function(strategy) {
if (!this.isPending()) {
throw new Error('FunctionCall in progress.');
}
precond.checkState(this.isPending(), 'FunctionCall in progress.');
this.strategy_ = strategy;
return this;
return this; // Return this for chaining.
};
/**
* Returns all intermediary results returned by the wrapped function since
* the initial call.
* @return An array of intermediary results.
*/
FunctionCall.prototype.getResults = function() {
return this.results_.concat();
// Returns all intermediary results returned by the wrapped function since
// the initial call.
FunctionCall.prototype.getLastResult = function() {
return this.lastResult_.concat();
};
/**
* Sets the backoff limit.
* @param maxNumberOfRetry The maximum number of backoffs.
* @return Itself for chaining.
*/
// Returns the number of times the wrapped function call was retried.
FunctionCall.prototype.getNumRetries = function() {
return this.numRetries_;
};
// Sets the backoff limit.
FunctionCall.prototype.failAfter = function(maxNumberOfRetry) {
if (!this.isPending()) {
throw new Error('FunctionCall in progress.');
}
precond.checkState(this.isPending(), 'FunctionCall in progress.');
this.failAfter_ = maxNumberOfRetry;
return this;
return this; // Return this for chaining.
};
/**
* Aborts the call.
*/
// Aborts the call.
FunctionCall.prototype.abort = function() {
if (this.isCompleted()) {
throw new Error('FunctionCall already completed.');
}
precond.checkState(!this.isCompleted(), 'FunctionCall already completed.');

@@ -149,13 +109,7 @@ if (this.isRunning()) {

/**
* Initiates the call to the wrapped function.
* @param backoffFactory Optional factory function used to create the backoff
* instance.
*/
// Initiates the call to the wrapped function. Accepts an optional factory
// function used to create the backoff instance; used when testing.
FunctionCall.prototype.start = function(backoffFactory) {
if (this.isAborted()) {
throw new Error('FunctionCall aborted.');
} else if (!this.isPending()) {
throw new Error('FunctionCall already started.');
}
precond.checkState(!this.isAborted(), 'FunctionCall aborted.');
precond.checkState(this.isPending(), 'FunctionCall already started.');

@@ -168,3 +122,3 @@ var strategy = this.strategy_ || new FibonacciBackoffStrategy();

this.backoff_.on('ready', this.doCall_.bind(this));
this.backoff_.on('ready', this.doCall_.bind(this, true /* isRetry */));
this.backoff_.on('fail', this.doCallback_.bind(this));

@@ -178,10 +132,10 @@ this.backoff_.on('backoff', this.handleBackoff_.bind(this));

this.state_ = FunctionCall.State_.RUNNING;
this.doCall_();
this.doCall_(false /* isRetry */);
};
/**
* Calls the wrapped function.
* @private
*/
FunctionCall.prototype.doCall_ = function() {
// Calls the wrapped function.
FunctionCall.prototype.doCall_ = function(isRetry) {
if (isRetry) {
this.numRetries_++;
}
var eventArgs = ['call'].concat(this.arguments_);

@@ -193,17 +147,10 @@ events.EventEmitter.prototype.emit.apply(this, eventArgs);

/**
* Calls the wrapped function's callback with the last result returned by the
* wrapped function.
* @private
*/
// Calls the wrapped function's callback with the last result returned by the
// wrapped function.
FunctionCall.prototype.doCallback_ = function() {
var args = this.results_[this.results_.length - 1];
this.callback_.apply(null, args);
this.callback_.apply(null, this.lastResult_);
};
/**
* Handles wrapped function's completion. This method acts as a replacement
* for the original callback function.
* @private
*/
// Handles wrapped function's completion. This method acts as a replacement
// for the original callback function.
FunctionCall.prototype.handleFunctionCallback_ = function() {

@@ -215,3 +162,3 @@ if (this.isAborted()) {

var args = Array.prototype.slice.call(arguments);
this.results_.push(args); // Save callback arguments.
this.lastResult_ = args; // Save last callback arguments.
events.EventEmitter.prototype.emit.apply(this, ['callback'].concat(args));

@@ -227,9 +174,3 @@

/**
* Handles backoff event.
* @param number Backoff number.
* @param delay Backoff delay.
* @param err The error that caused the backoff.
* @private
*/
// Handles the backoff event by reemitting it.
FunctionCall.prototype.handleBackoff_ = function(number, delay, err) {

@@ -236,0 +177,0 @@ this.emit('backoff', number, delay, err);

@@ -1,5 +0,3 @@

/*
* Copyright (c) 2012 Mathieu Turcotte
* Licensed under the MIT license.
*/
// Copyright (c) 2012 Mathieu Turcotte
// Licensed under the MIT license.

@@ -10,6 +8,3 @@ var util = require('util');

/**
* Exponential backoff strategy.
* @extends BackoffStrategy
*/
// Exponential backoff strategy.
function ExponentialBackoffStrategy(options) {

@@ -22,3 +17,2 @@ BackoffStrategy.call(this, options);

/** @inheritDoc */
ExponentialBackoffStrategy.prototype.next_ = function() {

@@ -30,3 +24,2 @@ this.backoffDelay_ = Math.min(this.nextBackoffDelay_, this.getMaxDelay());

/** @inheritDoc */
ExponentialBackoffStrategy.prototype.reset_ = function() {

@@ -33,0 +26,0 @@ this.backoffDelay_ = 0;

@@ -1,5 +0,3 @@

/*
* Copyright (c) 2012 Mathieu Turcotte
* Licensed under the MIT license.
*/
// Copyright (c) 2012 Mathieu Turcotte
// Licensed under the MIT license.

@@ -10,6 +8,3 @@ var util = require('util');

/**
* Fibonacci backoff strategy.
* @extends BackoffStrategy
*/
// Fibonacci backoff strategy.
function FibonacciBackoffStrategy(options) {

@@ -22,3 +17,2 @@ BackoffStrategy.call(this, options);

/** @inheritDoc */
FibonacciBackoffStrategy.prototype.next_ = function() {

@@ -31,3 +25,2 @@ var backoffDelay = Math.min(this.nextBackoffDelay_, this.getMaxDelay());

/** @inheritDoc */
FibonacciBackoffStrategy.prototype.reset_ = function() {

@@ -34,0 +27,0 @@ this.nextBackoffDelay_ = this.getInitialDelay();

@@ -1,5 +0,3 @@

/*
* Copyright (c) 2012 Mathieu Turcotte
* Licensed under the MIT license.
*/
// Copyright (c) 2012 Mathieu Turcotte
// Licensed under the MIT license.

@@ -13,11 +11,10 @@ var events = require('events');

/**
* Abstract class defining the skeleton for all backoff strategies.
* @param options Backoff strategy options.
* @param options.randomisationFactor The randomisation factor, must be between
* 0 and 1.
* @param options.initialDelay The backoff initial delay, in milliseconds.
* @param options.maxDelay The backoff maximal delay, in milliseconds.
* @constructor
*/
// Abstract class defining the skeleton for the backoff strategies. Accepts an
// object holding the options for the backoff strategy:
//
// * `randomisationFactor`: The randomisation factor which must be between 0
// and 1 where 1 equates to a randomization factor of 100% and 0 to no
// randomization.
// * `initialDelay`: The backoff initial delay in milliseconds.
// * `maxDelay`: The backoff maximal delay in milliseconds.
function BackoffStrategy(options) {

@@ -48,6 +45,3 @@ options = options || {};

/**
* Retrieves the maximal backoff delay.
* @return The maximal backoff delay, in milliseconds.
*/
// Gets the maximal backoff delay.
BackoffStrategy.prototype.getMaxDelay = function() {

@@ -57,6 +51,3 @@ return this.maxDelay_;

/**
* Retrieves the initial backoff delay.
* @return The initial backoff delay, in milliseconds.
*/
// Gets the initial backoff delay.
BackoffStrategy.prototype.getInitialDelay = function() {

@@ -66,6 +57,4 @@ return this.initialDelay_;

/**
* Template method that computes the next backoff delay.
* @return The backoff delay, in milliseconds.
*/
// Template method that computes and returns the next backoff delay in
// milliseconds.
BackoffStrategy.prototype.next = function() {

@@ -78,7 +67,4 @@ var backoffDelay = this.next_();

/**
* Computes the next backoff delay.
* @return The backoff delay, in milliseconds.
* @protected
*/
// Computes and returns the next backoff delay. Intended to be overridden by
// subclasses.
BackoffStrategy.prototype.next_ = function() {

@@ -88,5 +74,3 @@ throw new Error('BackoffStrategy.next_() unimplemented.');

/**
* Template method that resets the backoff delay to its initial value.
*/
// Template method that resets the backoff delay to its initial value.
BackoffStrategy.prototype.reset = function() {

@@ -96,6 +80,4 @@ this.reset_();

/**
* Resets the backoff delay to its initial value.
* @protected
*/
// Resets the backoff delay to its initial value. Intended to be overridden by
// subclasses.
BackoffStrategy.prototype.reset_ = function() {

@@ -102,0 +84,0 @@ throw new Error('BackoffStrategy.reset_() unimplemented.');

{
"name": "backoff",
"description": "Fibonacci and exponential backoffs.",
"version": "2.3.0",
"version": "2.4.0",
"license": "MIT",

@@ -12,9 +12,12 @@ "author": "Mathieu Turcotte <turcotte.mat@gmail.com>",

},
"dependencies": {
"precond": "0.2"
},
"devDependencies": {
"sinon": "1.7",
"nodeunit": "0.8",
"jshint": "2.0"
"sinon": "1.10",
"nodeunit": "0.9"
},
"scripts": {
"pretest": "node_modules/jshint/bin/jshint lib/ tests/ examples/ index.js",
"docco" : "docco lib/*.js lib/strategy/* index.js",
"pretest": "jshint lib/ tests/ examples/ index.js",
"test": "node_modules/nodeunit/bin/nodeunit tests/"

@@ -24,3 +27,7 @@ },

"node": ">= 0.6"
}
},
"file": [
"index.js",
"lib"
]
}

@@ -93,3 +93,3 @@ # Backoff for Node.js

var call = backoff.call(get, 'https://duplika.ca/', function(err, res) {
console.log('Retries: ' + call.getResults().length);
console.log('Num retries: ' + call.getNumRetries());

@@ -296,17 +296,25 @@ if (err) {

#### call.getResults()
#### call.getLastResult()
Retrieves all intermediary results returned by the wrapped function. This
Retrieves the last intermediary result 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.
Returns an array containing the results returned by the last wrapped function
call. For example, to get the error code returned by the last call, one would
do the following.
``` js
var results = call.getResults();
var error = results[1][0];
var results = call.getLastResult();
// The error code is the first parameter of the callback.
var error = results[0];
```
#### call.getNumRetries()
Returns the number of times the wrapped function call was retried. For a
wrapped function that succeeded immediately, this would return 0. 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.
#### call.start()

@@ -322,5 +330,5 @@

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.
The last result can be retrieved using `call.getLastResult()`. 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.

@@ -347,4 +355,8 @@ #### Event: 'call'

## Annotated source code
The annotated source code can be found at [mathieuturcotte.github.io/node-backoff/docs](http://mathieuturcotte.github.io/node-backoff/docs/).
## License
This code is free to use under the terms of the [MIT license](http://mturcotte.mit-license.org/).

@@ -111,3 +111,3 @@ /*

backoff.failAfter(0);
}, /must be greater than 0/);
}, /greater than 0 but got 0/);
test.done();

@@ -114,0 +114,0 @@ },

@@ -39,3 +39,3 @@ /*

new FunctionCall(1, [], function() {});
}, /should be a function/);
}, /Expected fn to be a function./);
test.done();

@@ -47,3 +47,3 @@ },

new FunctionCall(function() {}, [], 3);
}, /should be a function/);
}, /Expected callback to be a function./);
test.done();

@@ -53,3 +53,5 @@ },

"isPending should return false once the call is started": function(test) {
this.wrappedFn.yields(new Error()).yields(null, 'Success!');
this.wrappedFn.
onFirstCall().yields(new Error()).
onSecondCall().yields(null, 'Success!');
var call = new FunctionCall(this.wrappedFn, [], this.callback);

@@ -69,3 +71,5 @@

"isRunning should return true when call is in progress": function(test) {
this.wrappedFn.yields(new Error()).yields(null, 'Success!');
this.wrappedFn.
onFirstCall().yields(new Error()).
onSecondCall().yields(null, 'Success!');
var call = new FunctionCall(this.wrappedFn, [], this.callback);

@@ -85,3 +89,5 @@

"isCompleted should return true once the call completes": function(test) {
this.wrappedFn.yields(new Error()).yields(null, 'Success!');
this.wrappedFn.
onFirstCall().yields(new Error()).
onSecondCall().yields(null, 'Success!');
var call = new FunctionCall(this.wrappedFn, [], this.callback);

@@ -101,3 +107,5 @@

"isAborted should return true once the call is aborted": function(test) {
this.wrappedFn.yields(new Error()).yields(null, 'Success!');
this.wrappedFn.
onFirstCall().yields(new Error()).
onSecondCall().yields(null, 'Success!');
var call = new FunctionCall(this.wrappedFn, [], this.callback);

@@ -190,6 +198,7 @@

var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);
this.wrappedFn.yields(new Error())
.yields(new Error())
.yields(new Error())
.yields(null, 'Success!');
this.wrappedFn.
onCall(0).yields(new Error()).
onCall(1).yields(new Error()).
onCall(2).yields(new Error()).
onCall(3).yields(null, 'Success!');

@@ -242,3 +251,3 @@ call.start(this.backoffFactory);

"getResults should return intermediary results": function(test) {
"getLastResult should return the last intermediary result": function(test) {
var call = new FunctionCall(this.wrappedFn, [], this.callback);

@@ -251,2 +260,3 @@ this.wrappedFn.yields(1);

this.backoff.emit('ready');
test.deepEqual([i], call.getLastResult());
}

@@ -256,7 +266,28 @@

this.backoff.emit('ready');
test.deepEqual([null], call.getLastResult());
test.deepEqual([[1], [2], [3], [4], [null]], call.getResults());
test.done();
},
"getNumRetries should return the number of retries": function(test) {
var call = new FunctionCall(this.wrappedFn, [], this.callback);
this.wrappedFn.yields(1);
call.start(this.backoffFactory);
// The inital call doesn't count as a retry.
test.equals(0, call.getNumRetries());
for (var i = 2; i < 5; i++) {
this.wrappedFn.yields(i);
this.backoff.emit('ready');
test.equals(i - 1, call.getNumRetries());
}
this.wrappedFn.yields(null);
this.backoff.emit('ready');
test.equals(4, call.getNumRetries());
test.done();
},
"wrapped function's errors should be propagated": function(test) {

@@ -263,0 +294,0 @@ var call = new FunctionCall(this.wrappedFn, [1, 2, 3], this.callback);

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc