var events = require('events');
var precond = require('precond');
var util = require('util');
pax_global_header 0000666 0000000 0000000 00000000064 12646567512 0014530 g ustar 00root root 0000000 0000000 52 comment=8783cb24304c81062c7966766a5534e8fe95d9c4 node-backoff-2.5.0/ 0000775 0000000 0000000 00000000000 12646567512 0014052 5 ustar 00root root 0000000 0000000 node-backoff-2.5.0/.gitignore 0000664 0000000 0000000 00000000016 12646567512 0016037 0 ustar 00root root 0000000 0000000 node_modules/ node-backoff-2.5.0/.jshintrc 0000664 0000000 0000000 00000011437 12646567512 0015705 0 ustar 00root root 0000000 0000000 // Based on jshint defaults: http://goo.gl/OpjUs { // If the scan should stop on first error. "passfail": false, // Maximum errors before stopping. "maxerr": 50, // Predefined globals // If the standard browser globals should be predefined. "browser": false, // If the Node.js environment globals should be predefined. "node": true, // If the Rhino environment globals should be predefined. "rhino": false, // If CouchDB globals should be predefined. "couch": false, // If the Windows Scripting Host environment globals should be predefined. "wsh": false, // If jQuery globals should be predefined. "jquery": false, // If Prototype and Scriptaculous globals should be predefined. "prototypejs": false, // If MooTools globals should be predefined. "mootools": false, // If Dojo Toolkit globals should be predefined. "dojo": false, // Custom predefined globals. "predef": [], // Development // If debugger statements should be allowed. "debug": false, // If logging globals should be predefined (console, alert, etc.). "devel": false, // ECMAScript 5 // If ES5 syntax should be allowed. "es5": false, // Require the "use strict"; pragma. "strict": false, // If global "use strict"; should be allowed (also enables strict). "globalstrict": false, // The Good Parts // If automatic semicolon insertion should be tolerated. "asi": false, // If line breaks should not be checked, e.g. `return [\n] x`. "laxbreak": false, // If bitwise operators (&, |, ^, etc.) should not be allowed. "bitwise": false, // If assignments inside if, for and while should be allowed. Usually // conditions and loops are for comparison, not assignments. "boss": true, // If curly braces around all blocks should be required. "curly": true, // If === should be required. "eqeqeq": false, // If == null comparisons should be tolerated. "eqnull": false, // If eval should be allowed. "evil": true, // If ExpressionStatement should be allowed as Programs. "expr": false, // If `for in` loops must filter with `hasOwnPrototype`. "forin": false, // If immediate invocations must be wrapped in parens, e.g. // `( function(){}() );`. "immed": false, // If use before define should not be tolerated. "latedef": false, // If functions should be allowed to be defined within loops. "loopfunc": false, // If arguments.caller and arguments.callee should be disallowed. "noarg": false, // If the . should not be allowed in regexp literals. "regexp": false, // If unescaped first/last dash (-) inside brackets should be tolerated. "regexdash": false, // If script-targeted URLs should be tolerated. "scripturl": false, // If variable shadowing should be tolerated. "shadow": false, // If `new function () { ... };` and `new Object;` should be tolerated. "supernew": false, // If variables should be declared before used. "undef": true, // If `this` inside a non-constructor function is valid. "validthis": false, // If smarttabs should be tolerated // (http://www.emacswiki.org/emacs/SmartTabs). "smarttabs": false, // If the `__proto__` property should be allowed. "proto": false, // If one case switch statements should be allowed. "onecase": false, // If non-standard (but widely adopted) globals should be predefined. "nonstandard": false, // Allow multiline strings. "multistr": false, // If line breaks should not be checked around commas. "laxcomma": false, // If semicolons may be ommitted for the trailing statements inside of a // one-line blocks. "lastsemic": false, // If the `__iterator__` property should be allowed. "iterator": false, // If only function scope should be used for scope tests. "funcscope": false, // If es.next specific syntax should be allowed. "esnext": false, // Style preferences // If constructor names must be capitalized. "newcap": true, // If empty blocks should be disallowed. "noempty": false, // If using `new` for side-effects should be disallowed. "nonew": false, // If names should be checked for leading or trailing underscores // (object._attribute would be disallowed). "nomen": false, // If only one var statement per function should be allowed. "onevar": false, // If increment and decrement (`++` and `--`) should not be allowed. "plusplus": false, // If all forms of subscript notation are tolerated. "sub": true, // If trailing whitespace rules apply. "trailing": true, // If strict whitespace rules apply. "white": false, // Specify indentation. "indent": 4 } node-backoff-2.5.0/.travis.yml 0000664 0000000 0000000 00000000253 12646567512 0016163 0 ustar 00root root 0000000 0000000 language: node_js before_install: - npm install -g npm - npm install -g jshint node_js: - "node" - "iojs" notifications: email: - turcotte.mat@gmail.com node-backoff-2.5.0/CHANGES.md 0000664 0000000 0000000 00000004670 12646567512 0015453 0 ustar 00root root 0000000 0000000 # Changelog ## 2.X Those changes are not released yet. - In the functional API, invoke the wrapped function callback on abort and emit an `abort` event. This makes it possible to detect when abort is called. - Add a method on the function API, `call.retryIf(predicate)`, which specifies a predicate used to determine whether a given error is retriable or not. The default behavior is unaffected, errors remain retriable by default. ## 2.4.1 - Add support for specifying the factor to use in the `ExponentialStrategy`. ## 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 - Add four new methods to `FunctionCall` to query the state of the call. - isPending - isRunning - isCompleted - isAborted ## 2.2.0 - To match `Backoff` default behavior, `FunctionCall` no longer sets a default failAfter of 5, i.e. the maximum number of backoffs is now unbounded by default. ## 2.1.0 - `Backoff.backoff` now accepts an optional error argument that is re-emitted as the last argument of the `backoff` and `fail` events. This provides some context to the listeners as to why a given backoff operation was attempted. - The `backoff` event emitted by the `FunctionCall` class now contains, as its last argument, the error that caused the backoff operation to be attempted. This provides some context to the listeners as to why a given backoff operation was attempted. ## 2.0.0 - `FunctionCall.call` renamed into `FunctionCall.start`. - `backoff.call` no longer invokes the wrapped function on `nextTick`. That way, the first attempt is not delayed until the end of the current event loop. ## 1.2.1 - Make `FunctionCall.backoffFactory` a private member. ## 1.2.0 - Add `backoff.call` and the associated `FunctionCall` class. ## 1.1.0 - Add a `Backoff.failAfter`. ## 1.0.0 - Rename `start` and `done` events `backoff` and `ready`. - Remove deprecated `backoff.fibonnaci`. ## 0.2.1 - Create `backoff.fibonacci`. - Deprecate `backoff.fibonnaci`. - Expose fibonacci and exponential strategies. ## 0.2.0 - Provide exponential and fibonacci backoffs. ## 0.1.0 - Change `initialTimeout` and `maxTimeout` to `initialDelay` and `maxDelay`. - Use fibonnaci backoff. node-backoff-2.5.0/LICENSE 0000664 0000000 0000000 00000002044 12646567512 0015057 0 ustar 00root root 0000000 0000000 Copyright (C) 2012 Mathieu Turcotte Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. node-backoff-2.5.0/README.md 0000664 0000000 0000000 00000026027 12646567512 0015340 0 ustar 00root root 0000000 0000000 # Backoff for Node.js [](http://travis-ci.org/MathieuTurcotte/node-backoff) [](http://badge.fury.io/js/backoff) Fibonacci and exponential backoffs for Node.js. ## Installation ``` npm install backoff ``` ## Unit tests ``` npm test ``` ## Usage ### Object Oriented The usual way to instantiate a new `Backoff` object is to use one predefined factory method: `backoff.fibonacci([options])`, `backoff.exponential([options])`. `Backoff` inherits from `EventEmitter`. When a backoff starts, a `backoff` event is emitted and, when a backoff ends, a `ready` event is emitted. Handlers for these two events are called with the current backoff number and delay. ``` js var backoff = require('backoff'); var fibonacciBackoff = backoff.fibonacci({ randomisationFactor: 0, initialDelay: 10, maxDelay: 300 }); fibonacciBackoff.failAfter(10); fibonacciBackoff.on('backoff', function(number, delay) { // Do something when backoff starts, e.g. show to the // user the delay before next reconnection attempt. console.log(number + ' ' + delay + 'ms'); }); fibonacciBackoff.on('ready', function(number, delay) { // Do something when backoff ends, e.g. retry a failed // operation (DNS lookup, API call, etc.). If it fails // again then backoff, otherwise reset the backoff // instance. fibonacciBackoff.backoff(); }); fibonacciBackoff.on('fail', function() { // Do something when the maximum number of backoffs is // reached, e.g. ask the user to check its connection. console.log('fail'); }); fibonacciBackoff.backoff(); ``` The previous example would print the following. ``` 0 10ms 1 10ms 2 20ms 3 30ms 4 50ms 5 80ms 6 130ms 7 210ms 8 300ms 9 300ms fail ``` Note that `Backoff` objects are meant to be instantiated once and reused several times by calling `reset` after a successful "retry". ### 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, ...], callback)`. Typical usage looks like the following. ``` js var call = backoff.call(get, 'https://duplika.ca/', function(err, res) { console.log('Num retries: ' + call.getNumRetries()); if (err) { console.log('Error: ' + err.message); } else { console.log('Status: ' + res.statusCode); } }); call.retryIf(function(err) { return err.status == 503; }); call.setStrategy(new backoff.ExponentialStrategy()); call.failAfter(10); call.start(); ``` ## API ### backoff.fibonacci([options]) Constructs a Fibonacci backoff (10, 10, 20, 30, 50, etc.). The options are the following. - randomisationFactor: defaults to 0, must be between 0 and 1 - initialDelay: defaults to 100 ms - maxDelay: defaults to 10000 ms With these values, the backoff delay will increase from 100 ms to 10000 ms. The randomisation factor controls the range of randomness and must be between 0 and 1. By default, no randomisation is applied on the backoff delay. ### backoff.exponential([options]) Constructs an exponential backoff (10, 20, 40, 80, etc.). The options are the following. - randomisationFactor: defaults to 0, must be between 0 and 1 - initialDelay: defaults to 100 ms - maxDelay: defaults to 10000 ms - factor: defaults to 2, must be greater than 1 With these values, the backoff delay will increase from 100 ms to 10000 ms. The randomisation factor controls the range of randomness and must be between 0 and 1. By default, no randomisation is applied on the backoff delay. ### backoff.call(fn, [args, ...], callback) - fn: function to call in a backoff handler, i.e. the wrapped function - args: function's arguments - callback: function's callback accepting an error as its first argument Constructs a `FunctionCall` instance for the given function. The wrapped function will get retried until it succeds or reaches the maximum number of backoffs. In both cases, the callback function will be invoked with the last result returned by the wrapped function. It is the caller's responsability to initiate the call by invoking the `start` method on the returned `FunctionCall` instance. ### Class Backoff #### new Backoff(strategy) - strategy: the backoff strategy to use Constructs a new backoff object from a specific backoff strategy. The backoff strategy must implement the `BackoffStrategy`interface defined bellow. #### backoff.failAfter(numberOfBackoffs) - numberOfBackoffs: maximum number of backoffs before the fail event gets emitted, must be greater than 0 Sets a limit on the maximum number of backoffs that can be performed before a fail event gets emitted and the backoff instance is reset. By default, there is no limit on the number of backoffs that can be performed. #### backoff.backoff([err]) Starts a backoff operation. If provided, the error parameter will be emitted as the last argument of the `backoff` and `fail` events to let the listeners know why the backoff operation was attempted. An error will be thrown if a backoff operation is already in progress. In practice, this method should be called after a failed attempt to perform a sensitive operation (connecting to a database, downloading a resource over the network, etc.). #### backoff.reset() Resets the backoff delay to the initial backoff delay and stop any backoff operation in progress. After reset, a backoff instance can and should be reused. In practice, this method should be called after having successfully completed the sensitive operation guarded by the backoff instance or if the client code request to stop any reconnection attempt. #### Event: 'backoff' - number: number of backoffs since last reset, starting at 0 - delay: backoff delay in milliseconds - err: optional error parameter passed to `backoff.backoff([err])` Emitted when a backoff operation is started. Signals to the client how long the next backoff delay will be. #### Event: 'ready' - number: number of backoffs since last reset, starting at 0 - delay: backoff delay in milliseconds Emitted when a backoff operation is done. Signals that the failing operation should be retried. #### Event: 'fail' - err: optional error parameter passed to `backoff.backoff([err])` Emitted when the maximum number of backoffs is reached. This event will only be emitted if the client has set a limit on the number of backoffs by calling `backoff.failAfter(numberOfBackoffs)`. The backoff instance is automatically reset after this event is emitted. ### Interface BackoffStrategy A backoff strategy must provide the following methods. #### strategy.next() Computes and returns the next backoff delay. #### strategy.reset() Resets the backoff delay to its initial value. ### Class ExponentialStrategy Exponential (10, 20, 40, 80, etc.) backoff strategy implementation. #### new ExponentialStrategy([options]) The options are the following. - randomisationFactor: defaults to 0, must be between 0 and 1 - initialDelay: defaults to 100 ms - maxDelay: defaults to 10000 ms - factor: defaults to 2, must be greater than 1 ### Class FibonacciStrategy Fibonnaci (10, 10, 20, 30, 50, etc.) backoff strategy implementation. #### new FibonacciStrategy([options]) The options are the following. - randomisationFactor: defaults to 0, must be between 0 and 1 - initialDelay: defaults to 100 ms - maxDelay: defaults to 10000 ms ### 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.isPending() Returns whether the call is pending, i.e. hasn't been started. #### call.isRunning() Returns whether the call is in progress. #### call.isCompleted() Returns whether the call is completed. #### call.isAborted() Returns whether the call is aborted. #### call.setStrategy(strategy) - strategy: strategy instance to use, defaults to `FibonacciStrategy`. Sets the backoff strategy to use. This method should be called before `call.start()` otherwise an exception will be thrown. #### call.failAfter(maxNumberOfBackoffs) - maxNumberOfBackoffs: maximum number of backoffs before the call is aborted Sets the maximum number of backoffs before the call is aborted. By default, there is no limit on the number of backoffs that can be performed. This method should be called before `call.start()` otherwise an exception will be thrown.. #### call.retryIf(predicate) - predicate: a function which takes in as its argument the error returned by the wrapped function and determines whether it is retriable. Sets the predicate which will be invoked to determine whether a given error should be retried or not, e.g. a network error would be retriable while a type error would stop the function call. By default, all errors are considered to be retriable. This method should be called before `call.start()` otherwise an exception will be thrown. #### call.getLastResult() Returns an array containing the last arguments passed to the completion callback of the wrapped function. For example, to get the error code returned by the last call, one would do the following. ``` js var results = call.getLastResult(); // The error code is the first parameter of the callback. var error = results[0]; ``` Note that if the call was aborted, it will contain the abort error and not the last error returned by the wrapped function. #### 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() Initiates the call the wrapped function. This method should only be called once otherwise an exception will be thrown. #### call.abort() Aborts the call and causes the completion callback to be invoked with an abort error if the call was pending or running; does nothing otherwise. This method can safely be called mutliple times. #### 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 invokes its callback. #### Event: 'backoff' - number: backoff number, starts at 0 - delay: backoff delay in milliseconds - err: the error that triggered the backoff operation Emitted each time a backoff operation is started. #### Event: 'abort' Emitted when a call is aborted. ## 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/). node-backoff-2.5.0/docs/ 0000775 0000000 0000000 00000000000 12646567512 0015002 5 ustar 00root root 0000000 0000000 node-backoff-2.5.0/docs/backoff.html 0000664 0000000 0000000 00000020613 12646567512 0017265 0 ustar 00root root 0000000 0000000
Copyright (c) 2012 Mathieu Turcotte
Licensed under the MIT license.
var events = require('events');
var precond = require('precond');
var util = require('util');
A class to hold the state of a backoff operation. Accepts a backoff strategy to generate the backoff delays.
function Backoff(backoffStrategy) {
events.EventEmitter.call(this);
this.backoffStrategy_ = backoffStrategy;
this.maxNumberOfRetry_ = -1;
this.backoffNumber_ = 0;
this.backoffDelay_ = 0;
this.timeoutID_ = -1;
this.handlers = {
backoff: this.onBackoff_.bind(this)
};
}
util.inherits(Backoff, events.EventEmitter);
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) {
precond.checkArgument(maxNumberOfRetry > 0,
'Expected a maximum number of retry greater than 0 but got %s.',
maxNumberOfRetry);
this.maxNumberOfRetry_ = maxNumberOfRetry;
};
Starts a backoff operation. Accepts an optional parameter to let the listeners know why the backoff operation was started.
Backoff.prototype.backoff = function(err) {
precond.checkState(this.timeoutID_ === -1, 'Backoff in progress.');
if (this.backoffNumber_ === this.maxNumberOfRetry_) {
this.emit('fail', err);
this.reset();
} else {
this.backoffDelay_ = this.backoffStrategy_.next();
this.timeoutID_ = setTimeout(this.handlers.backoff, this.backoffDelay_);
this.emit('backoff', this.backoffNumber_, this.backoffDelay_, err);
}
};
Handles the backoff timeout completion.
Backoff.prototype.onBackoff_ = function() {
this.timeoutID_ = -1;
this.emit('ready', this.backoffNumber_, this.backoffDelay_);
this.backoffNumber_++;
};
Stops any backoff operation and resets the backoff delay to its inital value.
Backoff.prototype.reset = function() {
this.backoffNumber_ = 0;
this.backoffStrategy_.reset();
clearTimeout(this.timeoutID_);
this.timeoutID_ = -1;
};
module.exports = Backoff;
Copyright (c) 2012 Mathieu Turcotte
Licensed under the MIT license.
var util = require('util');
var precond = require('precond');
var BackoffStrategy = require('./strategy');
Exponential backoff strategy.
function ExponentialBackoffStrategy(options) {
BackoffStrategy.call(this, options);
this.backoffDelay_ = 0;
this.nextBackoffDelay_ = this.getInitialDelay();
this.factor_ = ExponentialBackoffStrategy.DEFAULT_FACTOR;
if (options && options.factor !== undefined) {
precond.checkArgument(options.factor > 1,
'Exponential factor should be greater than 1 but got %s.',
options.factor);
this.factor_ = options.factor;
}
}
util.inherits(ExponentialBackoffStrategy, BackoffStrategy);
Default multiplication factor used to compute the next backoff delay from the current one. The value can be overridden by passing a custom factor as part of the options.
ExponentialBackoffStrategy.DEFAULT_FACTOR = 2;
ExponentialBackoffStrategy.prototype.next_ = function() {
this.backoffDelay_ = Math.min(this.nextBackoffDelay_, this.getMaxDelay());
this.nextBackoffDelay_ = this.backoffDelay_ * this.factor_;
return this.backoffDelay_;
};
ExponentialBackoffStrategy.prototype.reset_ = function() {
this.backoffDelay_ = 0;
this.nextBackoffDelay_ = this.getInitialDelay();
};
module.exports = ExponentialBackoffStrategy;
Copyright (c) 2012 Mathieu Turcotte
Licensed under the MIT license.
var util = require('util');
var BackoffStrategy = require('./strategy');
Fibonacci backoff strategy.
function FibonacciBackoffStrategy(options) {
BackoffStrategy.call(this, options);
this.backoffDelay_ = 0;
this.nextBackoffDelay_ = this.getInitialDelay();
}
util.inherits(FibonacciBackoffStrategy, BackoffStrategy);
FibonacciBackoffStrategy.prototype.next_ = function() {
var backoffDelay = Math.min(this.nextBackoffDelay_, this.getMaxDelay());
this.nextBackoffDelay_ += this.backoffDelay_;
this.backoffDelay_ = backoffDelay;
return backoffDelay;
};
FibonacciBackoffStrategy.prototype.reset_ = function() {
this.nextBackoffDelay_ = this.getInitialDelay();
this.backoffDelay_ = 0;
};
module.exports = FibonacciBackoffStrategy;
Copyright (c) 2012 Mathieu Turcotte
Licensed under the MIT license.
var events = require('events');
var precond = require('precond');
var util = require('util');
var Backoff = require('./backoff');
var FibonacciBackoffStrategy = require('./strategy/fibonacci');
Wraps a function to be called in a backoff loop.
function FunctionCall(fn, args, callback) {
events.EventEmitter.call(this);
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.');
this.function_ = fn;
this.arguments_ = args;
this.callback_ = callback;
this.lastResult_ = [];
this.numRetries_ = 0;
this.backoff_ = null;
this.strategy_ = null;
this.failAfter_ = -1;
this.state_ = FunctionCall.State_.PENDING;
}
util.inherits(FunctionCall, events.EventEmitter);
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
};
Checks whether the call is pending.
FunctionCall.prototype.isPending = function() {
return this.state_ == FunctionCall.State_.PENDING;
};
Checks whether the call is in progress.
FunctionCall.prototype.isRunning = function() {
return this.state_ == FunctionCall.State_.RUNNING;
};
Checks whether the call is completed.
FunctionCall.prototype.isCompleted = function() {
return this.state_ == FunctionCall.State_.COMPLETED;
};
Checks whether the call is aborted.
FunctionCall.prototype.isAborted = function() {
return this.state_ == FunctionCall.State_.ABORTED;
};
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) {
precond.checkState(this.isPending(), 'FunctionCall in progress.');
this.strategy_ = strategy;
return this; // Return this for chaining.
};
Returns all intermediary results returned by the wrapped function since the initial call.
FunctionCall.prototype.getLastResult = function() {
return this.lastResult_.concat();
};
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) {
precond.checkState(this.isPending(), 'FunctionCall in progress.');
this.failAfter_ = maxNumberOfRetry;
return this; // Return this for chaining.
};
Aborts the call.
FunctionCall.prototype.abort = function() {
precond.checkState(!this.isCompleted(), 'FunctionCall already completed.');
if (this.isRunning()) {
this.backoff_.reset();
}
this.state_ = FunctionCall.State_.ABORTED;
};
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) {
precond.checkState(!this.isAborted(), 'FunctionCall aborted.');
precond.checkState(this.isPending(), 'FunctionCall already started.');
var strategy = this.strategy_ || new FibonacciBackoffStrategy();
this.backoff_ = backoffFactory ?
backoffFactory(strategy) :
new Backoff(strategy);
this.backoff_.on('ready', this.doCall_.bind(this, true /* isRetry */));
this.backoff_.on('fail', this.doCallback_.bind(this));
this.backoff_.on('backoff', this.handleBackoff_.bind(this));
if (this.failAfter_ > 0) {
this.backoff_.failAfter(this.failAfter_);
}
this.state_ = FunctionCall.State_.RUNNING;
this.doCall_(false /* isRetry */);
};
Calls the wrapped function.
FunctionCall.prototype.doCall_ = function(isRetry) {
if (isRetry) {
this.numRetries_++;
}
var eventArgs = ['call'].concat(this.arguments_);
events.EventEmitter.prototype.emit.apply(this, eventArgs);
var callback = this.handleFunctionCallback_.bind(this);
this.function_.apply(null, this.arguments_.concat(callback));
};
Calls the wrapped function’s callback with the last result returned by the wrapped function.
FunctionCall.prototype.doCallback_ = function() {
this.callback_.apply(null, this.lastResult_);
};
Handles wrapped function’s completion. This method acts as a replacement for the original callback function.
FunctionCall.prototype.handleFunctionCallback_ = function() {
if (this.isAborted()) {
return;
}
var args = Array.prototype.slice.call(arguments);
this.lastResult_ = args; // Save last callback arguments.
events.EventEmitter.prototype.emit.apply(this, ['callback'].concat(args));
if (args[0]) {
this.backoff_.backoff(args[0]);
} else {
this.state_ = FunctionCall.State_.COMPLETED;
this.doCallback_();
}
};
Handles the backoff event by reemitting it.
FunctionCall.prototype.handleBackoff_ = function(number, delay, err) {
this.emit('backoff', number, delay, err);
};
module.exports = FunctionCall;