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;