1. #1
    Sencha - Community Support Team mschwartz's Avatar
    Join Date
    Nov 2008
    Location
    San Diego, Peoples' Republic of California
    Posts
    2,053
    Vote Rating
    17
    mschwartz will become famous soon enough mschwartz will become famous soon enough

      0  

    Default A case where proper rendering order seems incorrect

    A case where proper rendering order seems incorrect


    I noticed a thread in the premium help forum about AJAX Not Acting Very Asynchronously.

    I've seen similar behaviour in Ext 3.0-rc1 that I think is related, though it's not doing any XHR type request.

    I'm using the maximgb TreeGrid panel for a grid/tree with 500+ nodes or so. I wanted to add "expand all" and "collapse all" buttons to the toolbar. I implemented expand all, and it was really really slow, and the UI appears active to the user so he might click on something while this slow operation is going on. I figured I'd put up a loadMask or an Ext.MessageBox.wait dialog to indicate the app was in fact doing something.

    It took me most of a day to get the busy indicator working, and it wasn't easy or obvious.

    My first attempt looked something like this:
    Code:
    var msg = Ext.MessageBox.wait('Expanding nodes...');
    store.each(function(node) { expand_node(); });
    msg.hide();
    No message box. It's as if the code wasn't even there. Step through it with firebug, and at the store.each line, the busy box does appear. Weird. Step over the store.each, and it goes away expanding nodes for 5+ seconds. The hide() works. But let it run full speed and no busy box.

    I tried using Ext.MessageBox.progress() and updateProgress() inside the store.each callback and no dialog appeared. Step through with firebug and the dialog appears and updates ("expanding 100 of 352", etc.).

    LoadMask didn't work either, unless I stepped through with firebug.

    I ended up implementing the code as shown below and it worked. But what a mess. I'd really like to understand why these things I tried didn't work. The code below has some optimization in it to not try to expand leaf nodes and incurring the 1ms defer for each of those.

    Code:
                            var total = store.getCount();
                            var msg = Ext.MessageBox.progress('Please wait', 'Expanding Nodes', '0 of ' + total);
                            var count = 0;
                            function ExpandOne() {
                                while (1) {
                                    var record = store.getAt(count);
                                    count++;
                                    Ext.MessageBox.updateProgress(count/total, count + ' of ' + total);
                                    if (!record.data._is_leaf) {
                                        store.expandNode(record);
                                        if (count < total) {
                                            ExpandOne.defer(1);
                                            return;
                                        }
                                        else {
                                            msg.hide();
                                            return;
                                        }
                                    }
                                    if (count >= total) {
                                        msg.hide();
                                        return;
                                    }
                                }
                            }
                            ExpandOne();

  2. #2
    Sencha - Community Support Team Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,246
    Vote Rating
    94
    Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of

      0  

    Default


    The problem is that javascript only has a single thread. If you are expanding all rows the thread will be occupied and even animated gifs won't start animating.

    Your solution adds defer(1) to stop the thread so other waiting tasks get executed before your task continues. Excellent solution I would say...

  3. #3
    Sencha - Community Support Team mschwartz's Avatar
    Join Date
    Nov 2008
    Location
    San Diego, Peoples' Republic of California
    Posts
    2,053
    Vote Rating
    17
    mschwartz will become famous soon enough mschwartz will become famous soon enough

      0  

    Default


    The behaviour observed would make sense if store.each() somehow returned right away and did its operations in the background - Ext.Ajax.request() returns right away.

    However, I looked at the code to store.each() which calls MixedCollection's each():

    Code:
        each : function(fn, scope){ 
            var items = [].concat(this.items); // each safe for removal 
            for(var i = 0, len = items.length; i < len; i++){ 
                if(fn.call(scope || items[i], items[i], i, len) === false){ 
                    break; 
                } 
            } 
        },
    I don't see it returning right away. It iterates over its items as expected.

  4. #4
    Sencha - Community Support Team mschwartz's Avatar
    Join Date
    Nov 2008
    Location
    San Diego, Peoples' Republic of California
    Posts
    2,053
    Vote Rating
    17
    mschwartz will become famous soon enough mschwartz will become famous soon enough

      0  

    Default


    Quote Originally Posted by Condor View Post
    The problem is that javascript only has a single thread. If you are expanding all rows the thread will be occupied and even animated gifs won't start animating.

    Your solution adds defer(1) to stop the thread so other waiting tasks get executed before your task continues. Excellent solution I would say...
    I know js has only a single thread (though the next generation of FF will have threads).

    What doesn't make sense is that Ext.MessageBox.wait() is called, it should do its DOM manipulation and the new DIVs and whatever else it adds to the DOM should appear before the store.each() gets a chance to run.

    Also, you can achieve something like multithreading using setTimeout, which is what I assume wait() does (to update the progress bar). The timer should interrupt the JS doing the expanding of the nodes, right?

  5. #5
    Sencha - Community Support Team Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,246
    Vote Rating
    94
    Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of

      0  

    Default


    No, a timer will not interrupt anything. It just adds a task to the queue which will be processed when the current thread finishes.

    Some browsers won't even update the display with updated information if the thread is running (this is probably what you were experiencing).

  6. #6
    Sencha - Community Support Team mschwartz's Avatar
    Join Date
    Nov 2008
    Location
    San Diego, Peoples' Republic of California
    Posts
    2,053
    Vote Rating
    17
    mschwartz will become famous soon enough mschwartz will become famous soon enough

      0  

    Default


    FYI

    Code:
    <html>
    <head>
    <title>Threading Test</title>
    <script type="text/javascript">
    var messages = [];
    var thread_count = 0;
    var t = null;
    function ThreadTest() {
        var el = document.getElementById('threadtest');
        messages.push('ThreadTest: ' + thread_count);
        thread_count++;
        if (thread_count > 100) {
            clearTimeout(t);
            el.innerHTML = messages.join('<br/>');
        }
    }
    function TestIt() {
        var el = document.getElementById('testit');
         t = setInterval(ThreadTest, 1);
        for (var i=0; i<100000; i++) {
            document.createElement('div');
        }
        messages.push('TestIt: ' + i);
        el.innerHTML = messages.join('<br/>');
    }
    
    </script>
    </head>
    <body onLoad="TestIt()">
    <b>TestIt</b><br/>
    <div id='testit'></div>
    <b>ThreadTest</b><br/>
    <div id='threadtest'></div>
    </body>
    </html>

  7. #7
    Sencha - Community Support Team mschwartz's Avatar
    Join Date
    Nov 2008
    Location
    San Diego, Peoples' Republic of California
    Posts
    2,053
    Vote Rating
    17
    mschwartz will become famous soon enough mschwartz will become famous soon enough

      0  

    Default


    Prints:
    TestIt
    TestIt: 100000
    ThreadTest
    TestIt: 100000
    ThreadTest: 0
    ThreadTest: 1
    ThreadTest: 2
    ThreadTest: 3
    ...
    In FF 3 and IE 7 and google chrome

  8. #8
    Sencha - Community Support Team mschwartz's Avatar
    Join Date
    Nov 2008
    Location
    San Diego, Peoples' Republic of California
    Posts
    2,053
    Vote Rating
    17
    mschwartz will become famous soon enough mschwartz will become famous soon enough

      0  

    Default


    This blogpost is very interesting:

    http://www.bluishcoder.co.nz/2006/06...narrative.html

Thread Participants: 1