UNPKG

7.4 kBJavaScriptView Raw
1var AWS = require('./core');
2
3/**
4 * @api private
5 * @!method on(eventName, callback)
6 * Registers an event listener callback for the event given by `eventName`.
7 * Parameters passed to the callback function depend on the individual event
8 * being triggered. See the event documentation for those parameters.
9 *
10 * @param eventName [String] the event name to register the listener for
11 * @param callback [Function] the listener callback function
12 * @param toHead [Boolean] attach the listener callback to the head of callback array if set to true.
13 * Default to be false.
14 * @return [AWS.SequentialExecutor] the same object for chaining
15 */
16AWS.SequentialExecutor = AWS.util.inherit({
17
18 constructor: function SequentialExecutor() {
19 this._events = {};
20 },
21
22 /**
23 * @api private
24 */
25 listeners: function listeners(eventName) {
26 return this._events[eventName] ? this._events[eventName].slice(0) : [];
27 },
28
29 on: function on(eventName, listener, toHead) {
30 if (this._events[eventName]) {
31 toHead ?
32 this._events[eventName].unshift(listener) :
33 this._events[eventName].push(listener);
34 } else {
35 this._events[eventName] = [listener];
36 }
37 return this;
38 },
39
40 onAsync: function onAsync(eventName, listener, toHead) {
41 listener._isAsync = true;
42 return this.on(eventName, listener, toHead);
43 },
44
45 removeListener: function removeListener(eventName, listener) {
46 var listeners = this._events[eventName];
47 if (listeners) {
48 var length = listeners.length;
49 var position = -1;
50 for (var i = 0; i < length; ++i) {
51 if (listeners[i] === listener) {
52 position = i;
53 }
54 }
55 if (position > -1) {
56 listeners.splice(position, 1);
57 }
58 }
59 return this;
60 },
61
62 removeAllListeners: function removeAllListeners(eventName) {
63 if (eventName) {
64 delete this._events[eventName];
65 } else {
66 this._events = {};
67 }
68 return this;
69 },
70
71 /**
72 * @api private
73 */
74 emit: function emit(eventName, eventArgs, doneCallback) {
75 if (!doneCallback) doneCallback = function() { };
76 var listeners = this.listeners(eventName);
77 var count = listeners.length;
78 this.callListeners(listeners, eventArgs, doneCallback);
79 return count > 0;
80 },
81
82 /**
83 * @api private
84 */
85 callListeners: function callListeners(listeners, args, doneCallback, prevError) {
86 var self = this;
87 var error = prevError || null;
88
89 function callNextListener(err) {
90 if (err) {
91 error = AWS.util.error(error || new Error(), err);
92 if (self._haltHandlersOnError) {
93 return doneCallback.call(self, error);
94 }
95 }
96 self.callListeners(listeners, args, doneCallback, error);
97 }
98
99 while (listeners.length > 0) {
100 var listener = listeners.shift();
101 if (listener._isAsync) { // asynchronous listener
102 listener.apply(self, args.concat([callNextListener]));
103 return; // stop here, callNextListener will continue
104 } else { // synchronous listener
105 try {
106 listener.apply(self, args);
107 } catch (err) {
108 error = AWS.util.error(error || new Error(), err);
109 }
110 if (error && self._haltHandlersOnError) {
111 doneCallback.call(self, error);
112 return;
113 }
114 }
115 }
116 doneCallback.call(self, error);
117 },
118
119 /**
120 * Adds or copies a set of listeners from another list of
121 * listeners or SequentialExecutor object.
122 *
123 * @param listeners [map<String,Array<Function>>, AWS.SequentialExecutor]
124 * a list of events and callbacks, or an event emitter object
125 * containing listeners to add to this emitter object.
126 * @return [AWS.SequentialExecutor] the emitter object, for chaining.
127 * @example Adding listeners from a map of listeners
128 * emitter.addListeners({
129 * event1: [function() { ... }, function() { ... }],
130 * event2: [function() { ... }]
131 * });
132 * emitter.emit('event1'); // emitter has event1
133 * emitter.emit('event2'); // emitter has event2
134 * @example Adding listeners from another emitter object
135 * var emitter1 = new AWS.SequentialExecutor();
136 * emitter1.on('event1', function() { ... });
137 * emitter1.on('event2', function() { ... });
138 * var emitter2 = new AWS.SequentialExecutor();
139 * emitter2.addListeners(emitter1);
140 * emitter2.emit('event1'); // emitter2 has event1
141 * emitter2.emit('event2'); // emitter2 has event2
142 */
143 addListeners: function addListeners(listeners) {
144 var self = this;
145
146 // extract listeners if parameter is an SequentialExecutor object
147 if (listeners._events) listeners = listeners._events;
148
149 AWS.util.each(listeners, function(event, callbacks) {
150 if (typeof callbacks === 'function') callbacks = [callbacks];
151 AWS.util.arrayEach(callbacks, function(callback) {
152 self.on(event, callback);
153 });
154 });
155
156 return self;
157 },
158
159 /**
160 * Registers an event with {on} and saves the callback handle function
161 * as a property on the emitter object using a given `name`.
162 *
163 * @param name [String] the property name to set on this object containing
164 * the callback function handle so that the listener can be removed in
165 * the future.
166 * @param (see on)
167 * @return (see on)
168 * @example Adding a named listener DATA_CALLBACK
169 * var listener = function() { doSomething(); };
170 * emitter.addNamedListener('DATA_CALLBACK', 'data', listener);
171 *
172 * // the following prints: true
173 * console.log(emitter.DATA_CALLBACK == listener);
174 */
175 addNamedListener: function addNamedListener(name, eventName, callback, toHead) {
176 this[name] = callback;
177 this.addListener(eventName, callback, toHead);
178 return this;
179 },
180
181 /**
182 * @api private
183 */
184 addNamedAsyncListener: function addNamedAsyncListener(name, eventName, callback, toHead) {
185 callback._isAsync = true;
186 return this.addNamedListener(name, eventName, callback, toHead);
187 },
188
189 /**
190 * Helper method to add a set of named listeners using
191 * {addNamedListener}. The callback contains a parameter
192 * with a handle to the `addNamedListener` method.
193 *
194 * @callback callback function(add)
195 * The callback function is called immediately in order to provide
196 * the `add` function to the block. This simplifies the addition of
197 * a large group of named listeners.
198 * @param add [Function] the {addNamedListener} function to call
199 * when registering listeners.
200 * @example Adding a set of named listeners
201 * emitter.addNamedListeners(function(add) {
202 * add('DATA_CALLBACK', 'data', function() { ... });
203 * add('OTHER', 'otherEvent', function() { ... });
204 * add('LAST', 'lastEvent', function() { ... });
205 * });
206 *
207 * // these properties are now set:
208 * emitter.DATA_CALLBACK;
209 * emitter.OTHER;
210 * emitter.LAST;
211 */
212 addNamedListeners: function addNamedListeners(callback) {
213 var self = this;
214 callback(
215 function() {
216 self.addNamedListener.apply(self, arguments);
217 },
218 function() {
219 self.addNamedAsyncListener.apply(self, arguments);
220 }
221 );
222 return this;
223 }
224});
225
226/**
227 * {on} is the prefered method.
228 * @api private
229 */
230AWS.SequentialExecutor.prototype.addListener = AWS.SequentialExecutor.prototype.on;
231
232/**
233 * @api private
234 */
235module.exports = AWS.SequentialExecutor;