sequential_executor.js 7.2 KB

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