Name: js-handler/node_modules/restify/node_modules/backoff/lib/function_call.js
| 1: | /* |
| 2: | * Copyright (c) 2012 Mathieu Turcotte |
| 3: | * Licensed under the MIT license. |
| 4: | */ |
| 5: | |
| 6: | var events = require('events'); |
| 7: | var util = require('util'); |
| 8: | |
| 9: | var Backoff = require('./backoff'); |
| 10: | var FibonacciBackoffStrategy = require('./strategy/fibonacci'); |
| 11: | |
| 12: | /** |
| 13: | * Returns true if the specified value is a function |
| 14: | * @param val Variable to test. |
| 15: | * @return Whether variable is a function. |
| 16: | */ |
| 17: | function isFunction(val) { |
| 18: | return typeof val == 'function'; |
| 19: | } |
| 20: | |
| 21: | /** |
| 22: | * Manages the calling of a function in a backoff loop. |
| 23: | * @param fn Function to wrap in a backoff handler. |
| 24: | * @param args Array of function's arguments. |
| 25: | * @param callback Function's callback. |
| 26: | * @constructor |
| 27: | */ |
| 28: | function FunctionCall(fn, args, callback) { |
| 29: | events.EventEmitter.call(this); |
| 30: | |
| 31: | if (!isFunction(fn)) { |
| 32: | throw new Error('fn should be a function.' + |
| 33: | 'Actual: ' + typeof fn); |
| 34: | } |
| 35: | |
| 36: | if (!isFunction(callback)) { |
| 37: | throw new Error('callback should be a function.' + |
| 38: | 'Actual: ' + typeof fn); |
| 39: | } |
| 40: | |
| 41: | this.function_ = fn; |
| 42: | this.arguments_ = args; |
| 43: | this.callback_ = callback; |
| 44: | this.results_ = []; |
| 45: | |
| 46: | this.backoff_ = null; |
| 47: | this.strategy_ = null; |
| 48: | this.failAfter_ = -1; |
| 49: | |
| 50: | this.called_ = false; |
| 51: | this.aborted_ = false; |
| 52: | } |
| 53: | util.inherits(FunctionCall, events.EventEmitter); |
| 54: | |
| 55: | /** |
| 56: | * Creates a backoff instance from the provided strategy; defaults to a |
| 57: | * Fibonacci backoff strategy if none is provided. |
| 58: | * @param strategy Optional strategy to use when instantiating the backoff. |
| 59: | * @return A backoff instance. |
| 60: | * @private |
| 61: | */ |
| 62: | FunctionCall.backoffFactory_ = function(strategy) { |
| 63: | return new Backoff(strategy || new FibonacciBackoffStrategy()); |
| 64: | }; |
| 65: | |
| 66: | /** |
| 67: | * Sets the backoff strategy. |
| 68: | * @param strategy The backoff strategy to use. |
| 69: | * @return Itself for chaining. |
| 70: | */ |
| 71: | FunctionCall.prototype.setStrategy = function(strategy) { |
| 72: | if (this.called_) { |
| 73: | throw new Error('Call in progress.'); |
| 74: | } |
| 75: | this.strategy_ = strategy; |
| 76: | return this; |
| 77: | }; |
| 78: | |
| 79: | /** |
| 80: | * Returns all intermediary results returned by the wrapped function since |
| 81: | * the initial call. |
| 82: | * @return An array of intermediary results. |
| 83: | */ |
| 84: | FunctionCall.prototype.getResults = function() { |
| 85: | return this.results_.concat(); |
| 86: | }; |
| 87: | |
| 88: | /** |
| 89: | * Sets the backoff limit. |
| 90: | * @param maxNumberOfRetry The maximum number of backoffs. |
| 91: | * @return Itself for chaining. |
| 92: | */ |
| 93: | FunctionCall.prototype.failAfter = function(maxNumberOfRetry) { |
| 94: | if (this.called_) { |
| 95: | throw new Error('Call in progress.'); |
| 96: | } |
| 97: | this.failAfter_ = maxNumberOfRetry; |
| 98: | return this; |
| 99: | }; |
| 100: | |
| 101: | /** |
| 102: | * Aborts the current call. |
| 103: | */ |
| 104: | FunctionCall.prototype.abort = function() { |
| 105: | this.aborted_ = true; |
| 106: | if (this.called_) { |
| 107: | this.backoff_.reset(); |
| 108: | } |
| 109: | }; |
| 110: | |
| 111: | /** |
| 112: | * Initiates the call to the wrapped function. |
| 113: | * @param backoffFactory Optional factory method used to create the backoff |
| 114: | * instance. |
| 115: | */ |
| 116: | FunctionCall.prototype.start = function(backoffFactory) { |
| 117: | if (this.aborted_) { |
| 118: | return; |
| 119: | } |
| 120: | |
| 121: | if (this.called_) { |
| 122: | throw new Error('Call in progress.'); |
| 123: | } |
| 124: | |
| 125: | backoffFactory = backoffFactory || FunctionCall.backoffFactory_; |
| 126: | |
| 127: | this.backoff_ = backoffFactory(this.strategy_); |
| 128: | this.backoff_.on('ready', this.doCall_.bind(this)); |
| 129: | this.backoff_.on('fail', this.doCallback_.bind(this)); |
| 130: | this.backoff_.on('backoff', this.handleBackoff_.bind(this)); |
| 131: | |
| 132: | if (this.failAfter_ > 0) { |
| 133: | this.backoff_.failAfter(this.failAfter_); |
| 134: | } |
| 135: | |
| 136: | this.called_ = true; |
| 137: | this.doCall_(); |
| 138: | }; |
| 139: | |
| 140: | /** |
| 141: | * Calls the wrapped function. |
| 142: | * @private |
| 143: | */ |
| 144: | FunctionCall.prototype.doCall_ = function() { |
| 145: | var eventArgs = ['call'].concat(this.arguments_); |
| 146: | events.EventEmitter.prototype.emit.apply(this, eventArgs); |
| 147: | var callback = this.handleFunctionCallback_.bind(this); |
| 148: | this.function_.apply(null, this.arguments_.concat(callback)); |
| 149: | }; |
| 150: | |
| 151: | /** |
| 152: | * Calls the wrapped function's callback with the last result returned by the |
| 153: | * wrapped function. |
| 154: | * @private |
| 155: | */ |
| 156: | FunctionCall.prototype.doCallback_ = function() { |
| 157: | var args = this.results_[this.results_.length - 1]; |
| 158: | this.callback_.apply(null, args); |
| 159: | }; |
| 160: | |
| 161: | /** |
| 162: | * Handles wrapped function's completion. This method acts as a replacement |
| 163: | * for the original callback function. |
| 164: | * @private |
| 165: | */ |
| 166: | FunctionCall.prototype.handleFunctionCallback_ = function() { |
| 167: | if (this.aborted_) { |
| 168: | return; |
| 169: | } |
| 170: | |
| 171: | var args = Array.prototype.slice.call(arguments); |
| 172: | this.results_.push(args); // Save callback arguments. |
| 173: | events.EventEmitter.prototype.emit.apply(this, ['callback'].concat(args)); |
| 174: | |
| 175: | if (args[0]) { |
| 176: | this.backoff_.backoff(args[0]); |
| 177: | } else { |
| 178: | this.doCallback_(); |
| 179: | } |
| 180: | }; |
| 181: | |
| 182: | /** |
| 183: | * Handles backoff event. |
| 184: | * @param number Backoff number. |
| 185: | * @param delay Backoff delay. |
| 186: | * @param err The error that caused the backoff. |
| 187: | * @private |
| 188: | */ |
| 189: | FunctionCall.prototype.handleBackoff_ = function(number, delay, err) { |
| 190: | this.emit('backoff', number, delay, err); |
| 191: | }; |
| 192: | |
| 193: | module.exports = FunctionCall; |
