YUI 3.x Home -

YUI Library Examples: AsyncQueue: Building a UI with AsyncQueue

AsyncQueue: Building a UI with AsyncQueue

This example illustrates how to break up the initial rendering of an application UI into queued code chunks, yielding back to the browser regularly to draw portions of the UI as they are ready.

Note: This method is mostly useful for apps constructing complex DOM structures. While the DOM structure contained in this example is not complex, some artificial delays were injected to simulate process intensive operations that would normally cause such delays.

The module will be inserted here. Click the button below.

The Markup

The markup will start with just a place holder element for our application.

  1. <div id="demo">
  2. <p>The module will be inserted here. <em>Click the button below</em>.</p>
  3. </div>
  4.  
  5. <button id="init">Initialize Application</button>
<div id="demo">
    <p>The module will be inserted here.  <em>Click the button below</em>.</p>
</div>
 
<button id="init">Initialize Application</button>

And will end with the following markup (indented for readability):

  1. <div id="demo">
  2. <div class="yui-module">
  3. <div class="yui-hd">
  4. <h4>AsyncQueue Demo</h4>
  5. </div>
  6. <div class="yui-bd">
  7. <div class="yui-nav">
  8. <ul>
  9. <li><a href="#">Nav Lorem</a></li>
  10. <li><a href="#">Nav Ipsum</a></li>
  11. <li><a href="#">Nav Dolor</a></li>
  12. <li><a href="#">Nav Sit</a></li>
  13. </ul>
  14. </div>
  15. <div class="yui-content">
  16. <p>[ App content here ]</p>
  17. </div>
  18. </div>
  19. <div class="yui-ft">
  20. <p class="yui-status">(status message here)</p>
  21. </div>
  22. </div>
  23. </div>
  24.  
  25. <button id="init">Re-initialize Application</button>
<div id="demo">
    <div class="yui-module">
        <div class="yui-hd">
            <h4>AsyncQueue Demo</h4>
        </div>
        <div class="yui-bd">
            <div class="yui-nav">
                <ul>
                    <li><a href="#">Nav Lorem</a></li>
                    <li><a href="#">Nav Ipsum</a></li>
                    <li><a href="#">Nav Dolor</a></li>
                    <li><a href="#">Nav Sit</a></li>
                </ul>
            </div>
            <div class="yui-content">
                <p>[ App content here ]</p>
            </div>
        </div>
        <div class="yui-ft">
            <p class="yui-status">(status message here)</p>
        </div>
    </div>
</div>
 
<button id="init">Re-initialize Application</button>

The CSS

Some CSS is added to make it look like an application.

  1. #init {
  2. margin-top: 1em;
  3. }
  4.  
  5. #demo .yui-module {
  6. position: relative;
  7. width: 28em;
  8. }
  9. #demo .yui-module .yui-hd,
  10. #demo .yui-module .yui-bd,
  11. #demo .yui-module .yui-ft {
  12. margin: 0;
  13. padding: 1ex 1em;
  14. }
  15. #demo .yui-module .yui-hd {
  16. background: #406ED9;
  17. }
  18. #demo .yui-module .yui-hd h4 {
  19. color: #fff;
  20. margin: 0;
  21. }
  22. #demo .yui-module .yui-bd {
  23. background: #ABCEFF;
  24. border-left: 1px solid #7A97BB;
  25. border-right: 1px solid #7A97BB;
  26. height: 5em;
  27. padding-top: 4.5em;
  28. position: relative;
  29. overflow: hidden;
  30. text-align: center;
  31. }
  32. #demo .yui-module .yui-ft {
  33. background: #fff;
  34. border: 1px solid #7A97BB;
  35. border-top-color: #ccc;
  36. padding-right: 25px;
  37. }
  38. #demo .yui-module .yui-status {
  39. margin: 0;
  40. padding: 0 25px 0 0;
  41. height: 1.3em;
  42. }
  43. #demo .yui-module .yui-nav {
  44. background: #fff;
  45. border-bottom: 1px solid #ccc;
  46. left: 0;
  47. padding: .5em;
  48. position: absolute;
  49. width: 27em;
  50. }
  51. #demo .yui-module .yui-nav ul,
  52. #demo .yui-module .yui-nav li {
  53. display: inline;
  54. list-style: none;
  55. margin: 0;
  56. padding: 0;
  57. }
  58. #demo .yui-module .yui-nav a {
  59. color: #ffa928;
  60. padding: 0 1.1em;
  61. }
  62. #demo .yui-module .working {
  63. background: #fff url(http://l.yimg.com/a/i/nt/ic/ut/bsc/busyarr_1.gif) no-repeat 26em 50%;
  64. }
#init {
    margin-top: 1em;
}
 
#demo .yui-module {
    position: relative;
    width: 28em;
}
#demo .yui-module .yui-hd,
#demo .yui-module .yui-bd,
#demo .yui-module .yui-ft {
    margin: 0;
    padding: 1ex 1em;
}
#demo .yui-module .yui-hd {
    background: #406ED9;
}
#demo .yui-module .yui-hd h4 {
    color: #fff;
    margin: 0;
}
#demo .yui-module .yui-bd {
    background: #ABCEFF;
    border-left: 1px solid #7A97BB;
    border-right: 1px solid #7A97BB;
    height: 5em;
    padding-top: 4.5em;
    position: relative;
    overflow: hidden;
    text-align: center;
}
#demo .yui-module .yui-ft {
    background: #fff;
    border: 1px solid #7A97BB;
    border-top-color: #ccc;
    padding-right: 25px;
}
#demo .yui-module .yui-status {
    margin: 0;
    padding: 0 25px 0 0;
    height: 1.3em;
}
#demo .yui-module .yui-nav {
    background: #fff;
    border-bottom: 1px solid #ccc;
    left: 0;
    padding: .5em;
    position: absolute;
    width: 27em;
}
#demo .yui-module .yui-nav ul,
#demo .yui-module .yui-nav li {
    display: inline;
    list-style: none;
    margin: 0;
    padding: 0;
}
#demo .yui-module .yui-nav a {
    color: #ffa928;
    padding: 0 1.1em;
}
#demo .yui-module .working {
    background: #fff url(http://l.yimg.com/a/i/nt/ic/ut/bsc/busyarr_1.gif) no-repeat 26em 50%;
}

Example application structure

For this example, we'll create a simple application that we'll contain under the MyApp namespace. The basic structure of the namespace will be as follows:

  1. YUI({base:"../../build/", timeout: 10000}).use("anim", "async-queue",function (Y) {
  2.  
  3. var MyApp = {
  4. // the name of the application
  5. NAME : "AsyncQueue Demo",
  6.  
  7. // rendering AsyncQueue
  8. q : new Y.AsyncQueue(),
  9.  
  10. // cache of frequently used nodes in the DOM structure
  11. nodes : {
  12. root : null,
  13. status : null,
  14. nav : null,
  15. content : null,
  16. foot : null
  17. },
  18.  
  19. /*** Public API methods ***/
  20. // draws the UI in the specified container
  21. render : function (container) { ... },
  22.  
  23. // update the status bar at the bottom of the app
  24. setStatus : function (message,working) { ... },
  25.  
  26.  
  27. /*** private methods ***/
  28. // adds the basic app skeleton to the page
  29. _renderFramework : function () { ... },
  30.  
  31. // populates the navigation section
  32. _renderNav : function () { ... },
  33.  
  34. // populates the content section
  35. _renderContent : function () { ... }
  36. };
  37.  
  38. });
YUI({base:"../../build/", timeout: 10000}).use("anim", "async-queue",function (Y) {
 
var MyApp = {
    // the name of the application
    NAME : "AsyncQueue Demo",
 
    // rendering AsyncQueue
    q : new Y.AsyncQueue(),
 
    // cache of frequently used nodes in the DOM structure
    nodes : {
        root    : null,
        status  : null,
        nav     : null,
        content : null,
        foot    : null
    },
 
    /*** Public API methods ***/
    // draws the UI in the specified container
    render : function (container) { ... },
 
    // update the status bar at the bottom of the app
    setStatus : function (message,working) { ... },
 
 
    /*** private methods ***/
    // adds the basic app skeleton to the page
    _renderFramework : function () { ... },
 
    // populates the navigation section
    _renderNav : function () { ... },
 
    // populates the content section
    _renderContent : function () { ... }
};
 
});

The MyApp.render function will add the rendering methods to the MyApp.q AsyncQueue and set it in motion. Each of the methods will be executed in turn, yielding back to the browser between steps. So as each piece of the UI is assembled, the browser is then given the opportunity to draw it.

  1. ...
  2. render : function (container) {
  3. // If the application is currently rendered somewhere, destroy it first
  4. // by clearing the queue and adding the destroy method to run before
  5. // the default rendering operations.
  6. if (MyApp.nodes.root) {
  7. MyApp.q.stop();
  8.  
  9. MyApp.q.add(
  10. MyApp.destroy
  11. );
  12. }
  13.  
  14. // Add the rendering operations to the ops.render queue and call run()
  15. MyApp.q.add(
  16. // pass the container param to the callback using Y.bind
  17. Y.bind(MyApp._renderFramework, MyApp, container),
  18. MyApp._renderNav,
  19. MyApp._renderContent).run();
  20. },
  21. ...
    ...
    render : function (container) {
        // If the application is currently rendered somewhere, destroy it first
        // by clearing the queue and adding the destroy method to run before
        // the default rendering operations.
        if (MyApp.nodes.root) {
            MyApp.q.stop();
 
            MyApp.q.add(
                MyApp.destroy
            );
        }
 
        // Add the rendering operations to the ops.render queue and call run()
        MyApp.q.add(
            // pass the container param to the callback using Y.bind
            Y.bind(MyApp._renderFramework, MyApp, container),
            MyApp._renderNav,
            MyApp._renderContent).run();
    },
    ...

If there are any process intensive operations in the rendering steps, the UI generated in all previous steps will have been drawn by the browser before the heavy lifting begins. This way, the user will be shown a part of the UI and can begin to develop an understanding of its structure and operation while the rest of it is being constructed.

A note on artificial delays and animation

In this example, rather than include code that would spike your CPU, delays were simulated by inserting AsyncQueue callbacks with a timeout and a function that does nothing. There is a distinct difference between a delay caused by code execution and a delay caused by setTimeout. In the former case, the browser is busy and likely won't respond to user events (such as clicks) until the executing code has completed. In the latter, any number of JavaScript event threads may execute to completion in the intervening time.

The rendering methods include animations courtesy of Y.Anim. Anim is similar to AsyncQueue in that it also works by scheduling a callback (the application of the easing calculation) for repeated execution, yielding to the browser between each update. However, Anim's schedule lives entirely outside the AsyncQueue's schedule. Stopping or pausing an AsyncQueue will not stop or pause a Y.Anim instance that is run() from an AsyncQueue callback. Similarly, if a callback starts an animation, AsyncQueue will not wait for the animation to complete before executing the next queued callback.

Full Script Source

The complete code for this example includes the artificial delays added to MyApp.q in the render method.

  1. YUI({base:"../../build/", timeout: 10000}).use("anim", "async-queue",function (Y) {
  2.  
  3. var MyApp = {
  4. NAME : 'Asynchronous Queue Demo',
  5.  
  6. q : new Y.AsyncQueue(),
  7.  
  8. nodes : {
  9. root : null,
  10. status : null,
  11. nav : null,
  12. content : null,
  13. foot : null
  14. },
  15.  
  16. render : function (container) {
  17. if (MyApp.nodes.root) {
  18. MyApp.q.stop();
  19. }
  20.  
  21. // artificial delays have been inserted to simulate _renderNav or
  22. // _renderContent being process intensive and taking a while to complete
  23. MyApp.q.add(
  24. // pass the container param to the callback using Y.bind
  25. Y.bind(MyApp._renderFramework, MyApp, container),
  26. {fn: function () {}, timeout: 700}, // artificial delay
  27. MyApp._renderNav,
  28. {fn: function () {}, timeout: 700}, // artificial delay
  29. MyApp._renderContent).run();
  30. },
  31.  
  32. setStatus : function (message,working) {
  33. MyApp.nodes.status.setContent(message);
  34.  
  35. MyApp.nodes.foot[working?'addClass':'removeClass']('working');
  36. },
  37.  
  38. _renderFramework : function (container) {
  39. var root = MyApp.nodes.root = Y.one(container);
  40.  
  41. root.setContent(
  42. '<div class="yui-module">'+
  43. '<div class="yui-hd">'+
  44. '<h4>'+MyApp.NAME+'</h4>'+
  45. '</div>'+
  46. '<div class="yui-bd">'+
  47. '<div class="yui-nav"></div>'+
  48. '<div class="yui-content"></div>'+
  49. '</div>'+
  50. '<div class="yui-ft">'+
  51. '<p class="yui-status"></p>'+
  52. '</div>'+
  53. '</div>');
  54.  
  55. MyApp.nodes.status = root.one('p.yui-status');
  56. MyApp.nodes.nav = root.one('.yui-nav');
  57. MyApp.nodes.content = root.one('.yui-content');
  58. MyApp.nodes.foot = root.one('.yui-ft');
  59.  
  60. MyApp.nodes.nav.setStyle('top','-30px');
  61. MyApp.nodes.content.setStyle('opacity',0);
  62.  
  63. MyApp.setStatus('Loading...',true);
  64. },
  65.  
  66. _renderNav : function () {
  67. var nav = MyApp.nodes.nav;
  68. nav.appendChild(Y.Node.create(
  69. '<ul>'+
  70. '<li><a href="#">Nav Lorem</a></li>'+
  71. '<li><a href="#">Nav Ipsum</a></li>'+
  72. '<li><a href="#">Nav Dolor</a></li>'+
  73. '<li><a href="#">Nav Sit</a></li>'+
  74. '</ul>'));
  75.  
  76. new Y.Anim({
  77. node : nav,
  78. to : { top : 0 },
  79. duration : .3
  80. }).run();
  81. },
  82.  
  83. _renderContent : function () {
  84. var content = MyApp.nodes.content;
  85.  
  86. content.appendChild(Y.Node.create(
  87. '<p>[ App content here ]</p>'));
  88.  
  89. new Y.Anim({
  90. node : content,
  91. to : { opacity : 1 },
  92. duration : .8
  93. }).run();
  94.  
  95. MyApp.setStatus('App initialized',false);
  96. }
  97. };
  98.  
  99. Y.on('click',function (e) {
  100. e.preventDefault();
  101. this.set('text','Re-initialize Application');
  102.  
  103. MyApp.render('#demo');
  104. },'#init');
  105.  
  106. // expose the example structure
  107. YUI.example = { MyApp : MyApp };
  108.  
  109. });
YUI({base:"../../build/", timeout: 10000}).use("anim", "async-queue",function (Y) {
 
var MyApp = {
    NAME : 'Asynchronous Queue Demo',
 
    q : new Y.AsyncQueue(),
 
    nodes : {
        root    : null,
        status  : null,
        nav     : null,
        content : null,
        foot    : null
    },
 
    render : function (container) {
        if (MyApp.nodes.root) {
            MyApp.q.stop();
        }
 
        // artificial delays have been inserted to simulate _renderNav or
        // _renderContent being process intensive and taking a while to complete
        MyApp.q.add(
            // pass the container param to the callback using Y.bind
            Y.bind(MyApp._renderFramework, MyApp, container),
            {fn: function () {}, timeout: 700}, // artificial delay
            MyApp._renderNav,
            {fn: function () {}, timeout: 700}, // artificial delay
            MyApp._renderContent).run();
    },
 
    setStatus : function (message,working) {
        MyApp.nodes.status.setContent(message);
 
        MyApp.nodes.foot[working?'addClass':'removeClass']('working');
    },
 
    _renderFramework : function (container) {
        var root = MyApp.nodes.root = Y.one(container);
 
        root.setContent(
        '<div class="yui-module">'+
            '<div class="yui-hd">'+
                '<h4>'+MyApp.NAME+'</h4>'+
            '</div>'+
            '<div class="yui-bd">'+
                '<div class="yui-nav"></div>'+
                '<div class="yui-content"></div>'+
            '</div>'+
            '<div class="yui-ft">'+
                '<p class="yui-status"></p>'+
            '</div>'+
        '</div>');
 
        MyApp.nodes.status  = root.one('p.yui-status');
        MyApp.nodes.nav     = root.one('.yui-nav');
        MyApp.nodes.content = root.one('.yui-content');
        MyApp.nodes.foot    = root.one('.yui-ft');
 
        MyApp.nodes.nav.setStyle('top','-30px');
        MyApp.nodes.content.setStyle('opacity',0);
 
        MyApp.setStatus('Loading...',true);
    },
 
    _renderNav : function () {
        var nav = MyApp.nodes.nav;
        nav.appendChild(Y.Node.create(
            '<ul>'+
                '<li><a href="#">Nav Lorem</a></li>'+
                '<li><a href="#">Nav Ipsum</a></li>'+
                '<li><a href="#">Nav Dolor</a></li>'+
                '<li><a href="#">Nav Sit</a></li>'+
            '</ul>'));
 
        new Y.Anim({
            node : nav,
            to :   { top : 0 },
            duration : .3
        }).run();
    },
 
    _renderContent : function () {
        var content = MyApp.nodes.content;
 
        content.appendChild(Y.Node.create(
            '<p>[ App content here ]</p>'));
 
        new Y.Anim({
            node : content,
            to :   { opacity : 1 },
            duration : .8
        }).run();
 
        MyApp.setStatus('App initialized',false);
    }
};
 
Y.on('click',function (e) {
    e.preventDefault();
    this.set('text','Re-initialize Application');
 
    MyApp.render('#demo');
},'#init');
 
// expose the example structure
YUI.example = { MyApp : MyApp };
 
});

Copyright © 2009 Yahoo! Inc. All rights reserved.

Privacy Policy - Terms of Service - Copyright Policy - Job Openings