Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

async-queue-debug.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*
  2. Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.com/yui/license.html
  5. version: 3.4.0
  6. build: nightly
  7. */
  8. YUI.add('async-queue', function(Y) {
  9. /**
  10. * <p>AsyncQueue allows you create a chain of function callbacks executed
  11. * via setTimeout (or synchronously) that are guaranteed to run in order.
  12. * Items in the queue can be promoted or removed. Start or resume the
  13. * execution chain with run(). pause() to temporarily delay execution, or
  14. * stop() to halt and clear the queue.</p>
  15. *
  16. * @module async-queue
  17. */
  18. /**
  19. * <p>A specialized queue class that supports scheduling callbacks to execute
  20. * sequentially, iteratively, even asynchronously.</p>
  21. *
  22. * <p>Callbacks can be function refs or objects with the following keys. Only
  23. * the <code>fn</code> key is required.</p>
  24. *
  25. * <ul>
  26. * <li><code>fn</code> -- The callback function</li>
  27. * <li><code>context</code> -- The execution context for the callbackFn.</li>
  28. * <li><code>args</code> -- Arguments to pass to callbackFn.</li>
  29. * <li><code>timeout</code> -- Millisecond delay before executing callbackFn.
  30. * (Applies to each iterative execution of callback)</li>
  31. * <li><code>iterations</code> -- Number of times to repeat the callback.
  32. * <li><code>until</code> -- Repeat the callback until this function returns
  33. * true. This setting trumps iterations.</li>
  34. * <li><code>autoContinue</code> -- Set to false to prevent the AsyncQueue from
  35. * executing the next callback in the Queue after
  36. * the callback completes.</li>
  37. * <li><code>id</code> -- Name that can be used to get, promote, get the
  38. * indexOf, or delete this callback.</li>
  39. * </ul>
  40. *
  41. * @class AsyncQueue
  42. * @extends EventTarget
  43. * @constructor
  44. * @param callback* {Function|Object} 0..n callbacks to seed the queue
  45. */
  46. Y.AsyncQueue = function() {
  47. this._init();
  48. this.add.apply(this, arguments);
  49. };
  50. var Queue = Y.AsyncQueue,
  51. EXECUTE = 'execute',
  52. SHIFT = 'shift',
  53. PROMOTE = 'promote',
  54. REMOVE = 'remove',
  55. isObject = Y.Lang.isObject,
  56. isFunction = Y.Lang.isFunction;
  57. /**
  58. * <p>Static default values used to populate callback configuration properties.
  59. * Preconfigured defaults include:</p>
  60. *
  61. * <ul>
  62. * <li><code>autoContinue</code>: <code>true</code></li>
  63. * <li><code>iterations</code>: 1</li>
  64. * <li><code>timeout</code>: 10 (10ms between callbacks)</li>
  65. * <li><code>until</code>: (function to run until iterations &lt;= 0)</li>
  66. * </ul>
  67. *
  68. * @property defaults
  69. * @type {Object}
  70. * @static
  71. */
  72. Queue.defaults = Y.mix({
  73. autoContinue : true,
  74. iterations : 1,
  75. timeout : 10,
  76. until : function () {
  77. this.iterations |= 0;
  78. return this.iterations <= 0;
  79. }
  80. }, Y.config.queueDefaults || {});
  81. Y.extend(Queue, Y.EventTarget, {
  82. /**
  83. * Used to indicate the queue is currently executing a callback.
  84. *
  85. * @property _running
  86. * @type {Boolean|Object} true for synchronous callback execution, the
  87. * return handle from Y.later for async callbacks.
  88. * Otherwise false.
  89. * @protected
  90. */
  91. _running : false,
  92. /**
  93. * Initializes the AsyncQueue instance properties and events.
  94. *
  95. * @method _init
  96. * @protected
  97. */
  98. _init : function () {
  99. Y.EventTarget.call(this, { prefix: 'queue', emitFacade: true });
  100. this._q = [];
  101. /**
  102. * Callback defaults for this instance. Static defaults that are not
  103. * overridden are also included.
  104. *
  105. * @property defaults
  106. * @type {Object}
  107. */
  108. this.defaults = {};
  109. this._initEvents();
  110. },
  111. /**
  112. * Initializes the instance events.
  113. *
  114. * @method _initEvents
  115. * @protected
  116. */
  117. _initEvents : function () {
  118. this.publish({
  119. 'execute' : { defaultFn : this._defExecFn, emitFacade: true },
  120. 'shift' : { defaultFn : this._defShiftFn, emitFacade: true },
  121. 'add' : { defaultFn : this._defAddFn, emitFacade: true },
  122. 'promote' : { defaultFn : this._defPromoteFn, emitFacade: true },
  123. 'remove' : { defaultFn : this._defRemoveFn, emitFacade: true }
  124. });
  125. },
  126. /**
  127. * Returns the next callback needing execution. If a callback is
  128. * configured to repeat via iterations or until, it will be returned until
  129. * the completion criteria is met.
  130. *
  131. * When the queue is empty, null is returned.
  132. *
  133. * @method next
  134. * @return {Function} the callback to execute
  135. */
  136. next : function () {
  137. var callback;
  138. while (this._q.length) {
  139. callback = this._q[0] = this._prepare(this._q[0]);
  140. if (callback && callback.until()) {
  141. this.fire(SHIFT, { callback: callback });
  142. callback = null;
  143. } else {
  144. break;
  145. }
  146. }
  147. return callback || null;
  148. },
  149. /**
  150. * Default functionality for the &quot;shift&quot; event. Shifts the
  151. * callback stored in the event object's <em>callback</em> property from
  152. * the queue if it is the first item.
  153. *
  154. * @method _defShiftFn
  155. * @param e {Event} The event object
  156. * @protected
  157. */
  158. _defShiftFn : function (e) {
  159. if (this.indexOf(e.callback) === 0) {
  160. this._q.shift();
  161. }
  162. },
  163. /**
  164. * Creates a wrapper function to execute the callback using the aggregated
  165. * configuration generated by combining the static AsyncQueue.defaults, the
  166. * instance defaults, and the specified callback settings.
  167. *
  168. * The wrapper function is decorated with the callback configuration as
  169. * properties for runtime modification.
  170. *
  171. * @method _prepare
  172. * @param callback {Object|Function} the raw callback
  173. * @return {Function} a decorated function wrapper to execute the callback
  174. * @protected
  175. */
  176. _prepare: function (callback) {
  177. if (isFunction(callback) && callback._prepared) {
  178. return callback;
  179. }
  180. var config = Y.merge(
  181. Queue.defaults,
  182. { context : this, args: [], _prepared: true },
  183. this.defaults,
  184. (isFunction(callback) ? { fn: callback } : callback)),
  185. wrapper = Y.bind(function () {
  186. if (!wrapper._running) {
  187. wrapper.iterations--;
  188. }
  189. if (isFunction(wrapper.fn)) {
  190. wrapper.fn.apply(wrapper.context || Y,
  191. Y.Array(wrapper.args));
  192. }
  193. }, this);
  194. return Y.mix(wrapper, config);
  195. },
  196. /**
  197. * Sets the queue in motion. All queued callbacks will be executed in
  198. * order unless pause() or stop() is called or if one of the callbacks is
  199. * configured with autoContinue: false.
  200. *
  201. * @method run
  202. * @return {AsyncQueue} the AsyncQueue instance
  203. * @chainable
  204. */
  205. run : function () {
  206. var callback,
  207. cont = true;
  208. for (callback = this.next();
  209. cont && callback && !this.isRunning();
  210. callback = this.next())
  211. {
  212. cont = (callback.timeout < 0) ?
  213. this._execute(callback) :
  214. this._schedule(callback);
  215. }
  216. if (!callback) {
  217. /**
  218. * Event fired after the last queued callback is executed.
  219. * @event complete
  220. */
  221. this.fire('complete');
  222. }
  223. return this;
  224. },
  225. /**
  226. * Handles the execution of callbacks. Returns a boolean indicating
  227. * whether it is appropriate to continue running.
  228. *
  229. * @method _execute
  230. * @param callback {Object} the callback object to execute
  231. * @return {Boolean} whether the run loop should continue
  232. * @protected
  233. */
  234. _execute : function (callback) {
  235. this._running = callback._running = true;
  236. callback.iterations--;
  237. this.fire(EXECUTE, { callback: callback });
  238. var cont = this._running && callback.autoContinue;
  239. this._running = callback._running = false;
  240. return cont;
  241. },
  242. /**
  243. * Schedules the execution of asynchronous callbacks.
  244. *
  245. * @method _schedule
  246. * @param callback {Object} the callback object to execute
  247. * @return {Boolean} whether the run loop should continue
  248. * @protected
  249. */
  250. _schedule : function (callback) {
  251. this._running = Y.later(callback.timeout, this, function () {
  252. if (this._execute(callback)) {
  253. this.run();
  254. }
  255. });
  256. return false;
  257. },
  258. /**
  259. * Determines if the queue is waiting for a callback to complete execution.
  260. *
  261. * @method isRunning
  262. * @return {Boolean} true if queue is waiting for a
  263. * from any initiated transactions
  264. */
  265. isRunning : function () {
  266. return !!this._running;
  267. },
  268. /**
  269. * Default functionality for the &quot;execute&quot; event. Executes the
  270. * callback function
  271. *
  272. * @method _defExecFn
  273. * @param e {Event} the event object
  274. * @protected
  275. */
  276. _defExecFn : function (e) {
  277. e.callback();
  278. },
  279. /**
  280. * Add any number of callbacks to the end of the queue. Callbacks may be
  281. * provided as functions or objects.
  282. *
  283. * @method add
  284. * @param callback* {Function|Object} 0..n callbacks
  285. * @return {AsyncQueue} the AsyncQueue instance
  286. * @chainable
  287. */
  288. add : function () {
  289. this.fire('add', { callbacks: Y.Array(arguments,0,true) });
  290. return this;
  291. },
  292. /**
  293. * Default functionality for the &quot;add&quot; event. Adds the callbacks
  294. * in the event facade to the queue. Callbacks successfully added to the
  295. * queue are present in the event's <code>added</code> property in the
  296. * after phase.
  297. *
  298. * @method _defAddFn
  299. * @param e {Event} the event object
  300. * @protected
  301. */
  302. _defAddFn : function(e) {
  303. var _q = this._q,
  304. added = [];
  305. Y.Array.each(e.callbacks, function (c) {
  306. if (isObject(c)) {
  307. _q.push(c);
  308. added.push(c);
  309. }
  310. });
  311. e.added = added;
  312. },
  313. /**
  314. * Pause the execution of the queue after the execution of the current
  315. * callback completes. If called from code outside of a queued callback,
  316. * clears the timeout for the pending callback. Paused queue can be
  317. * restarted with q.run()
  318. *
  319. * @method pause
  320. * @return {AsyncQueue} the AsyncQueue instance
  321. * @chainable
  322. */
  323. pause: function () {
  324. if (isObject(this._running)) {
  325. this._running.cancel();
  326. }
  327. this._running = false;
  328. return this;
  329. },
  330. /**
  331. * Stop and clear the queue after the current execution of the
  332. * current callback completes.
  333. *
  334. * @method stop
  335. * @return {AsyncQueue} the AsyncQueue instance
  336. * @chainable
  337. */
  338. stop : function () {
  339. this._q = [];
  340. return this.pause();
  341. },
  342. /**
  343. * Returns the current index of a callback. Pass in either the id or
  344. * callback function from getCallback.
  345. *
  346. * @method indexOf
  347. * @param callback {String|Function} the callback or its specified id
  348. * @return {Number} index of the callback or -1 if not found
  349. */
  350. indexOf : function (callback) {
  351. var i = 0, len = this._q.length, c;
  352. for (; i < len; ++i) {
  353. c = this._q[i];
  354. if (c === callback || c.id === callback) {
  355. return i;
  356. }
  357. }
  358. return -1;
  359. },
  360. /**
  361. * Retrieve a callback by its id. Useful to modify the configuration
  362. * while the queue is running.
  363. *
  364. * @method getCallback
  365. * @param id {String} the id assigned to the callback
  366. * @return {Object} the callback object
  367. */
  368. getCallback : function (id) {
  369. var i = this.indexOf(id);
  370. return (i > -1) ? this._q[i] : null;
  371. },
  372. /**
  373. * Promotes the named callback to the top of the queue. If a callback is
  374. * currently executing or looping (via until or iterations), the promotion
  375. * is scheduled to occur after the current callback has completed.
  376. *
  377. * @method promote
  378. * @param callback {String|Object} the callback object or a callback's id
  379. * @return {AsyncQueue} the AsyncQueue instance
  380. * @chainable
  381. */
  382. promote : function (callback) {
  383. var payload = { callback : callback },e;
  384. if (this.isRunning()) {
  385. e = this.after(SHIFT, function () {
  386. this.fire(PROMOTE, payload);
  387. e.detach();
  388. }, this);
  389. } else {
  390. this.fire(PROMOTE, payload);
  391. }
  392. return this;
  393. },
  394. /**
  395. * <p>Default functionality for the &quot;promote&quot; event. Promotes the
  396. * named callback to the head of the queue.</p>
  397. *
  398. * <p>The event object will contain a property &quot;callback&quot;, which
  399. * holds the id of a callback or the callback object itself.</p>
  400. *
  401. * @method _defPromoteFn
  402. * @param e {Event} the custom event
  403. * @protected
  404. */
  405. _defPromoteFn : function (e) {
  406. var i = this.indexOf(e.callback),
  407. promoted = (i > -1) ? this._q.splice(i,1)[0] : null;
  408. e.promoted = promoted;
  409. if (promoted) {
  410. this._q.unshift(promoted);
  411. }
  412. },
  413. /**
  414. * Removes the callback from the queue. If the queue is active, the
  415. * removal is scheduled to occur after the current callback has completed.
  416. *
  417. * @method remove
  418. * @param callback {String|Object} the callback object or a callback's id
  419. * @return {AsyncQueue} the AsyncQueue instance
  420. * @chainable
  421. */
  422. remove : function (callback) {
  423. var payload = { callback : callback },e;
  424. // Can't return the removed callback because of the deferral until
  425. // current callback is complete
  426. if (this.isRunning()) {
  427. e = this.after(SHIFT, function () {
  428. this.fire(REMOVE, payload);
  429. e.detach();
  430. },this);
  431. } else {
  432. this.fire(REMOVE, payload);
  433. }
  434. return this;
  435. },
  436. /**
  437. * <p>Default functionality for the &quot;remove&quot; event. Removes the
  438. * callback from the queue.</p>
  439. *
  440. * <p>The event object will contain a property &quot;callback&quot;, which
  441. * holds the id of a callback or the callback object itself.</p>
  442. *
  443. * @method _defRemoveFn
  444. * @param e {Event} the custom event
  445. * @protected
  446. */
  447. _defRemoveFn : function (e) {
  448. var i = this.indexOf(e.callback);
  449. e.removed = (i > -1) ? this._q.splice(i,1)[0] : null;
  450. },
  451. /**
  452. * Returns the number of callbacks in the queue.
  453. *
  454. * @method size
  455. * @return {Number}
  456. */
  457. size : function () {
  458. // next() flushes callbacks that have met their until() criteria and
  459. // therefore shouldn't count since they wouldn't execute anyway.
  460. if (!this.isRunning()) {
  461. this.next();
  462. }
  463. return this._q.length;
  464. }
  465. });
  466. }, '3.4.0' ,{requires:['event-custom']});