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