|
1 <!DOCTYPE html> |
|
2 <html lang="en"> |
|
3 <head> |
|
4 <meta charset="utf-8"> |
|
5 <title>AsyncQueue</title> |
|
6 <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic"> |
|
7 <link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css"> |
|
8 <link rel="stylesheet" href="../assets/css/main.css"> |
|
9 <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css"> |
|
10 <link rel="shortcut icon" type="image/png" href="../assets/favicon.png"> |
|
11 <script src="../../build/yui/yui-min.js"></script> |
|
12 |
|
13 </head> |
|
14 <body> |
|
15 <!-- |
|
16 <a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a> |
|
17 --> |
|
18 <div id="doc"> |
|
19 <div id="hd"> |
|
20 <h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1> |
|
21 </div> |
|
22 |
|
23 <a href="#toc" class="jump">Jump to Table of Contents</a> |
|
24 |
|
25 |
|
26 <h1>AsyncQueue</h1> |
|
27 <div class="yui3-g"> |
|
28 <div class="yui3-u-3-4"> |
|
29 <div id="main"> |
|
30 <div class="content"><div class="intro" class="component"> |
|
31 <p> |
|
32 AsyncQueue allows you create a chain of function callbacks executed via |
|
33 <code>setTimeout</code> that are guaranteed to run in order. This can |
|
34 enable progressive incremental rendering of your UI so your users can |
|
35 begin to see and interact with your page while the infrastructure is |
|
36 being built. Similarly, process-intensive operations that will lock up |
|
37 the UI while the JavaScript is being executed can be broken up into |
|
38 chunks, helping to keep your interface responsive. |
|
39 </p> |
|
40 </div> |
|
41 |
|
42 <h2 id="getting-started">Getting Started</h2> |
|
43 |
|
44 <p> |
|
45 To include the source files for AsyncQueue and its dependencies, first load |
|
46 the YUI seed file if you haven't already loaded it. |
|
47 </p> |
|
48 |
|
49 <pre class="code prettyprint"><script src="http://yui.yahooapis.com/3.10.3/build/yui/yui-min.js"></script></pre> |
|
50 |
|
51 |
|
52 <p> |
|
53 Next, create a new YUI instance for your application and populate it with the |
|
54 modules you need by specifying them as arguments to the <code>YUI().use()</code> method. |
|
55 YUI will automatically load any dependencies required by the modules you |
|
56 specify. |
|
57 </p> |
|
58 |
|
59 <pre class="code prettyprint"><script> |
|
60 // Create a new YUI instance and populate it with the required modules. |
|
61 YUI().use('async-queue', function (Y) { |
|
62 // AsyncQueue is available and ready for use. Add implementation |
|
63 // code here. |
|
64 }); |
|
65 </script></pre> |
|
66 |
|
67 |
|
68 <p> |
|
69 For more information on creating YUI instances and on the |
|
70 <a href="http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_use"><code>use()</code> method</a>, see the |
|
71 documentation for the <a href="../yui/index.html">YUI Global Object</a>. |
|
72 </p> |
|
73 |
|
74 |
|
75 <h2 id="using">Using AsyncQueue</h2> |
|
76 |
|
77 <h3 id="interacting">Creating and interacting with an AsyncQueue</h3> |
|
78 |
|
79 <p> |
|
80 AsyncQueues manage an array of callbacks that can be either simple function |
|
81 references or <a href="#callbacks">objects with specific keys</a>. The |
|
82 primary methods on AsyncQueue are <code>add</code> and |
|
83 <code>run</code>. |
|
84 </p> |
|
85 |
|
86 <p> |
|
87 When <code>run()</code> is invoked, each callback is executed in turn, |
|
88 either synchronously or via <code>setTimeout</code> (depending on the |
|
89 configuration of the callback or of the AsyncQueue instance). |
|
90 </p> |
|
91 |
|
92 <p> |
|
93 Queued callbacks can also be promoted to the top of the queue or removed |
|
94 from it. |
|
95 </p> |
|
96 |
|
97 <pre class="code prettyprint">var q = new Y.AsyncQueue(callbackB, someTask, callbackA, callbackC); |
|
98 q.add(callbackD, callbackE); // B, someTask, A, C, D, E |
|
99 q.promote(callbackA); // A, B, someTask, C, D, E |
|
100 q.remove(someTask); // A, B, C, D, E |
|
101 q.run(); // execute A, then B, then C, then D, then E</pre> |
|
102 |
|
103 |
|
104 |
|
105 <h4 id="stopping">Pausing and stopping an AsyncQueue</h4> |
|
106 |
|
107 <p> |
|
108 In addition to <code>run()</code>, AsyncQueue instances also have |
|
109 <code>pause()</code> and <code>stop()</code> methods to interrupt the run |
|
110 state. |
|
111 </p> |
|
112 |
|
113 <p> |
|
114 To wait for an external process to complete, such as an XHR request, call |
|
115 <code>pause()</code>, then <code>run()</code> again to resume |
|
116 execution. |
|
117 </p> |
|
118 |
|
119 <p> |
|
120 Call <code>stop()</code> to terminate execution and flush the AsyncQueue. |
|
121 </p> |
|
122 |
|
123 <pre class="code prettyprint">// Seed the instance with callbacks |
|
124 var q = new Y.AsyncQueue( |
|
125 MyApp.doSomething, |
|
126 |
|
127 // The second callback will pause the Queue and send an XHR for data |
|
128 function () { |
|
129 q.pause(); |
|
130 |
|
131 // Send the asynchronous XHR |
|
132 Y.io(MyApp.getDataUri(), { on: { |
|
133 success : function (xid,o) { |
|
134 try { |
|
135 var data = Y.JSON.parse(o.responseText); |
|
136 } |
|
137 catch (e) { |
|
138 MyApp.showErrorStatus(); |
|
139 q.stop(); |
|
140 } |
|
141 |
|
142 MyApp.processData(data); |
|
143 |
|
144 // In the XHR callback, restart the AsyncQueue if successful |
|
145 q.run(); |
|
146 }, |
|
147 failure : function () { |
|
148 MyApp.showErrorStatus(); |
|
149 // Stop the AsyncQueue if anything goes wrong |
|
150 q.stop(); |
|
151 } |
|
152 }}); |
|
153 }, |
|
154 |
|
155 // The third callback will do partial updates until complete |
|
156 { |
|
157 fn: Y.bind(MyApp.updateUI,MyApp), |
|
158 until: function () { |
|
159 return MyApp.remainingData >= 0; |
|
160 } |
|
161 }, |
|
162 MyApp.doSomethingElse); |
|
163 |
|
164 q.run();</pre> |
|
165 |
|
166 |
|
167 |
|
168 <h4 id="callbacks">About AsyncQueue callbacks</h4> |
|
169 |
|
170 <p> |
|
171 AsyncQueue callbacks can be simple function references or object literals |
|
172 with the following keys: |
|
173 </p> |
|
174 |
|
175 <table> |
|
176 <thead> |
|
177 <tr> |
|
178 <th>property</th> |
|
179 <th>description</th> |
|
180 <th>default</th> |
|
181 </tr> |
|
182 </thead> |
|
183 <tbody> |
|
184 <tr> |
|
185 <td><code>fn</code></td> |
|
186 <td><strong>Required</strong>. The callback function to execute.</td> |
|
187 <td>(none)</td> |
|
188 </tr> |
|
189 <tr> |
|
190 <td><code>context</code></td> |
|
191 <td>The context from which to execute the callback function.</td> |
|
192 <td>The AsyncQueue instance</td> |
|
193 </tr> |
|
194 <tr> |
|
195 <td><code>args</code></td> |
|
196 <td>Array of arguments that will be passed as individual args to the callback function.</td> |
|
197 <td>(none)</td> |
|
198 </tr> |
|
199 <tr> |
|
200 <td><code>timeout</code></td> |
|
201 <td>Millisecond delay before each execution of this callback. Set to -1 to trigger synchronous execution.</td> |
|
202 <td>10</td> |
|
203 </tr> |
|
204 <tr> |
|
205 <td><code>iterations</code></td> |
|
206 <td>The number of times to execute this callback before shifting it from the queue.</td> |
|
207 <td>1</td> |
|
208 </tr> |
|
209 <tr> |
|
210 <td><code>until</code></td> |
|
211 <td>A function that will return <code>true</code> when the current callback can be shifted from the queue.</td> |
|
212 <td>a function that tests against <code>iterations</code></td> |
|
213 </tr> |
|
214 <tr> |
|
215 <td><code>id</code></td> |
|
216 <td>Name given to this callback for ease of reference.</td> |
|
217 <td>(none)</td> |
|
218 </tr> |
|
219 <tr> |
|
220 <td><code>autoContinue</code></td> |
|
221 <td>Set to <code>false</code> to automatically <code>pause()</code> after this callback.</td> |
|
222 <td>true</td> |
|
223 </tr> |
|
224 </tbody> |
|
225 </table> |
|
226 |
|
227 <h4 id="defaults">Class- and instance-level callback defaults</h4> |
|
228 |
|
229 <p> |
|
230 AsyncQueue provides three places to configure callbacks (in decreasing |
|
231 precedence order): |
|
232 </p> |
|
233 |
|
234 <ol> |
|
235 <li>The callback object</li> |
|
236 <li>The AsyncQueue instance's <code>defaults</code> collection</li> |
|
237 <li>The class static <code>defaults</code> collection</li> |
|
238 </ol> |
|
239 |
|
240 <pre class="code prettyprint">// All AsyncQueue instances will execute all callbacks synchronously by default |
|
241 Y.AsyncQueue.defaults.timeout = -1; |
|
242 |
|
243 var q = new Y.AsyncQueue(); |
|
244 |
|
245 // run every callback in this instance twice before moving to the next callback |
|
246 q.defaults.iterations = 2; |
|
247 |
|
248 q.add(functionA, |
|
249 { |
|
250 fn: functionB, |
|
251 timeout: 100 // this callback will be executed asynchronously |
|
252 }); |
|
253 |
|
254 // functionA executes twice immediately, then after 100 milliseconds functionB |
|
255 // is executed, then after another 100ms functionB is executed again. |
|
256 q.run();</pre> |
|
257 |
|
258 |
|
259 |
|
260 <h4 id="sync">Synchronous mode for callback execution</h4> |
|
261 <p> |
|
262 One of the main goals of the AsyncQueue is to provide a mechanism to |
|
263 prevent process-intensive operations from locking up the UI. By default, |
|
264 AsyncQueue callbacks are executed via <code>setTimeout</code> to facilitate |
|
265 this. The <code>timeout</code> configuration accepts -1 as a value to |
|
266 trigger synchronous callback execution. Use this setting with caution. |
|
267 </p> |
|
268 |
|
269 <h4 id="chaining">About timeout chaining</h4> |
|
270 |
|
271 <p> |
|
272 Timeout chaining is a strategy to address the lack of <a |
|
273 href="http://en.wikipedia.org/wiki/Thread_(computer_science)">multithreading</a> |
|
274 in JavaScript. When complex or iterative code executes it can cause the |
|
275 page to stop responding until the running JavaScript process completes; it |
|
276 can also cause "non-responsive script" or "long-running script" dialogs to |
|
277 be presented to the user. Both outcomes are detrimental to user |
|
278 experience. |
|
279 </p> |
|
280 |
|
281 <p> |
|
282 To address this, the operation can be split into chunks, and |
|
283 <code>setTimeout</code> can be used to yield control back to other |
|
284 operations between each chunk. A common use case for this technique is to |
|
285 allow browser reflows to display DOM modifications incrementally while |
|
286 batches of work are being done in JavaScript. For iterative functions, the |
|
287 code can execute a portion of the overall work, then schedule itself to run |
|
288 via <code>setTimeout</code>. |
|
289 </p> |
|
290 |
|
291 <p>The basic form of an iterative timeout chain is:</p> |
|
292 |
|
293 <pre class="code prettyprint">(function () { |
|
294 |
|
295 /* do a chunk of the work */ |
|
296 |
|
297 if (/* process completion check fails */) { |
|
298 // Schedule myself for re-execution, picking up where I left off |
|
299 setTimeout(arguments.callee,0); |
|
300 } |
|
301 })();</pre> |
|
302 |
|
303 |
|
304 |
|
305 <p> |
|
306 When dealing with <code>setTimeout</code>, it's easy to introduce race |
|
307 conditions. Because all timeouts are scheduled against the same timer and |
|
308 only one can run at a time, when two timeouts are separately scheduled, it |
|
309 is possible for them to execute out of intended order. |
|
310 </p> |
|
311 |
|
312 <p> |
|
313 AsyncQueue supports both "chunked operations" (by specifying callback |
|
314 timeouts) and "iterative operations" (by specifying callback |
|
315 <code>iterations</code> or <code>until</code> functions). Furthermore, |
|
316 AsyncQueue manages the callback sequence and can therefore guarantee the |
|
317 execution order, so you avoid race conditions. |
|
318 </p> |
|
319 |
|
320 <h4 id="events">Exposed events</h4> |
|
321 <p> |
|
322 AsyncQueue is based on EventTarget and instances emit the following events |
|
323 throughout their lifecycle: |
|
324 </p> |
|
325 |
|
326 <table> |
|
327 <thead> |
|
328 <tr> |
|
329 <th>Event</th> |
|
330 <th>When</th> |
|
331 <th>Event payload</th> |
|
332 </tr> |
|
333 </thead> |
|
334 <tbody> |
|
335 <tr> |
|
336 <td><code>add</code></td> |
|
337 <td>Callbacks are added to the AsyncQueue.</td> |
|
338 <td><code>{ callbacks: (Array of callbacks added) }</code></td> |
|
339 </tr> |
|
340 <tr> |
|
341 <td><code>promote</code></td> |
|
342 <td>A callback is promoted.</td> |
|
343 <td><code>{ callback : (callback) }</code></td> |
|
344 </tr> |
|
345 <tr> |
|
346 <td><code>remove</code></td> |
|
347 <td>A callback is removed.</td> |
|
348 <td><code>{ callback : (callback) }</code></td> |
|
349 </tr> |
|
350 <tr> |
|
351 <td><code>execute</code></td> |
|
352 <td>A callback is executed.</td> |
|
353 <td><code>{ callback : (callback) }</code></td> |
|
354 </tr> |
|
355 <tr> |
|
356 <td><code>shift</code></td> |
|
357 <td>A callback is shifted from the AsyncQueue.</td> |
|
358 <td><code>{ callback : (callback) }</code></td> |
|
359 </tr> |
|
360 <tr> |
|
361 <td><code>complete</code></td> |
|
362 <td>After the last callback is finished executing. <em>NOT</em> fired after <code>stop()</code>.</td> |
|
363 <td>(none)</td> |
|
364 </tr> |
|
365 </tbody> |
|
366 </table> |
|
367 </div> |
|
368 </div> |
|
369 </div> |
|
370 |
|
371 <div class="yui3-u-1-4"> |
|
372 <div class="sidebar"> |
|
373 |
|
374 <div id="toc" class="sidebox"> |
|
375 <div class="hd"> |
|
376 <h2 class="no-toc">Table of Contents</h2> |
|
377 </div> |
|
378 |
|
379 <div class="bd"> |
|
380 <ul class="toc"> |
|
381 <li> |
|
382 <a href="#getting-started">Getting Started</a> |
|
383 </li> |
|
384 <li> |
|
385 <a href="#using">Using AsyncQueue</a> |
|
386 <ul class="toc"> |
|
387 <li> |
|
388 <a href="#interacting">Creating and interacting with an AsyncQueue</a> |
|
389 <ul class="toc"> |
|
390 <li> |
|
391 <a href="#stopping">Pausing and stopping an AsyncQueue</a> |
|
392 </li> |
|
393 <li> |
|
394 <a href="#callbacks">About AsyncQueue callbacks</a> |
|
395 </li> |
|
396 <li> |
|
397 <a href="#defaults">Class- and instance-level callback defaults</a> |
|
398 </li> |
|
399 <li> |
|
400 <a href="#sync">Synchronous mode for callback execution</a> |
|
401 </li> |
|
402 <li> |
|
403 <a href="#chaining">About timeout chaining</a> |
|
404 </li> |
|
405 <li> |
|
406 <a href="#events">Exposed events</a> |
|
407 </li> |
|
408 </ul> |
|
409 </li> |
|
410 </ul> |
|
411 </li> |
|
412 </ul> |
|
413 </div> |
|
414 </div> |
|
415 |
|
416 |
|
417 |
|
418 <div class="sidebox"> |
|
419 <div class="hd"> |
|
420 <h2 class="no-toc">Examples</h2> |
|
421 </div> |
|
422 |
|
423 <div class="bd"> |
|
424 <ul class="examples"> |
|
425 |
|
426 |
|
427 <li data-description="This example employs AsyncQueue to incrementally construct an application interface; this illustrates the approach you'd take to allow chunked rendering of the UI in a process-intensive application."> |
|
428 <a href="queue-app.html">Building a UI with AsyncQueue</a> |
|
429 </li> |
|
430 |
|
431 |
|
432 </ul> |
|
433 </div> |
|
434 </div> |
|
435 |
|
436 |
|
437 |
|
438 </div> |
|
439 </div> |
|
440 </div> |
|
441 </div> |
|
442 |
|
443 <script src="../assets/vendor/prettify/prettify-min.js"></script> |
|
444 <script>prettyPrint();</script> |
|
445 |
|
446 <script> |
|
447 YUI.Env.Tests = { |
|
448 examples: [], |
|
449 project: '../assets', |
|
450 assets: '../assets/async-queue', |
|
451 name: 'async-queue', |
|
452 title: 'AsyncQueue', |
|
453 newWindow: '', |
|
454 auto: false |
|
455 }; |
|
456 YUI.Env.Tests.examples.push('queue-app'); |
|
457 |
|
458 </script> |
|
459 <script src="../assets/yui/test-runner.js"></script> |
|
460 |
|
461 |
|
462 |
|
463 </body> |
|
464 </html> |