You found a bug! We've classified it as TOUCH-4552 . We encourage you to continue the discussion and to find an acceptable workaround while we work on a permanent fix.
  1. #1
    Sencha User
    Join Date
    Sep 2012
    Posts
    104
    Vote Rating
    0
    rbahumi is on a distinguished road

      0  

    Default Constructing list items from itemTpl takes long time and takes over the main UI thred

    Constructing list items from itemTpl takes long time and takes over the main UI thred


    REQUIRED INFORMATION




    Ext version tested:
    • Sencha Touch 2.2
    Browser versions tested against:
    • Chrome Canary
    • Native Android 4.0.3 webview
    Description:


    My List uses itemTpl, and every time it starts creating new items from a JSON object, the main UI thread is stuck until the new Dom is rebuild.
    It takes ~1sec to build the initial list, and 2.40sec to build the second batch of list items. (and this is on a powerful Intel i7, the Android is far worst...)
    • When looking at the Chrome-DevTools/Timeline/Frames - it shows that there are many sequences of "Recalculate Style" and then "Layout" records


      Each of these steps is performed over the whole document - which makes it more expensive:
      Layout scope Whole document
      In addition, there is an exclamation mark next to each "Layout" record, noting that it was forced to do so synchronously:
      Note Forced synchronous layout is a possible performance bottleneck.


      Each of these "forced-layout" is caused by a call to dom.offsetParent in sencha-touch.js::isPainted() function:
      Code:
      isPainted: (function() {
              return !Ext.browser.is.IE ? function() {
                  var dom = this.dom;
                  return Boolean(dom && dom.offsetParent);
              } : function() {
                  var dom = this.dom;
                  return Boolean(dom && (dom.offsetHeight !== 0 && dom.offsetWidth !== 0));
              }
          })(),


      Is calling dom.offsetParent and causing this Layout invalidation-force loop really necesary?
      Can the list items be constructed elsewhere, (optimally in another thread, or maybe with batched animationFrame calls) and then be appended to the list?
    • Note that this issue is even worst when using the ListPaging plugin, because the dom is already populated, and manipulating it takes much longer. Also, because this is synchronous,
      scrolling the list while it is being build is not possible.
    2.4sec frame.jpg

    Steps to reproduce the problem:
    • I will provide more data if asked, but this is not related to my specific implementation - this is the way you guys implemented the construction of lists.
    The result that was expected:
    • List build faster
    • List is build in the background, user can scroll the already existing list items while it loads
    The result that occurs instead:
    • The main UI thread is stuck until the list is fully constructed
    • The list is taking really long time to reconstruct
    Screenshot or Video:
    • attached
    Operating System:
    • Win 7
    • Android 4.0.3

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    37,206
    Vote Rating
    856
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    Thanks for the report! I have opened a bug in our bug tracker.

  3. #3
    Sencha User Jamie Avins's Avatar
    Join Date
    Mar 2007
    Location
    Redwood City, California
    Posts
    3,661
    Vote Rating
    20
    Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough

      0  

    Default


    Can you try this override to see if it improves for you:

    Code:
    Ext.define('Ext.OverrideDomHelper', {
        override: 'Ext.dom.Helper',
        insertHtml: function(where, el, html) {
            var setStart, range, frag, rangeEl, isBeforeBegin, isAfterBegin;
    
            where = where.toLowerCase();
    
            if (Ext.isTextNode(el)) {
                if (where == 'afterbegin' ) {
                    where = 'beforebegin';
                }
                else if (where == 'beforeend') {
                    where = 'afterend';
                }
            }
    
            isBeforeBegin = where == 'beforebegin';
            isAfterBegin = where == 'afterbegin';
    
            range = Ext.feature.has.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
            setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');
    
            if (isBeforeBegin || where == 'afterend') {
                if (range) {
                    range[setStart](el);
                    frag = range.createContextualFragment(html);
                }
                else {
                    frag = this.createContextualFragment(html);
                }
                el.parentNode.insertBefore(frag, isBeforeBegin ? el : el.nextSibling);
                return el[(isBeforeBegin ? 'previous' : 'next') + 'Sibling'];
            }
            else {
                rangeEl = (isAfterBegin ? 'first' : 'last') + 'Child';
                if (el.firstChild) {
                    if (range) {
                        // Creating ranges on a hidden element throws an error, checking for the element being painted is
                        // VERY expensive, so we'll catch the error and fall back to using the full fragment
                        try {
                            range[setStart](el[rangeEl]);
                            frag = range.createContextualFragment(html);
                        }
                        catch(e) {
                            frag = this.createContextualFragment(html);
                        }
                    } else {
                        frag = this.createContextualFragment(html);
                    }
    
                    if (isAfterBegin) {
                        el.insertBefore(frag, el.firstChild);
                    } else {
                        el.appendChild(frag);
                    }
                } else {
                    el.innerHTML = html;
                }
                return el[rangeEl];
            }
        }
    });

    Sencha Inc

    Jamie Avins

    @jamieavins

  4. #4
    Sencha User
    Join Date
    Sep 2012
    Posts
    104
    Vote Rating
    0
    rbahumi is on a distinguished road

      0  

    Default


    Well, I do see less 'Recalculate Style' & 'Layout' records at the beginning of the frame, but the overall performance time remained the same - the frame still lasts ~2.4 seconds.


    As a temporary solution I would like to ask:

    Is the number of records loaded to the Store on each API call (Ext.data.Store::pageSize configuration) has to be bounded to the number of items rendered in the Ext.dataview.List in each Batch?
    • On the Model aspect of MVC - It is better to load ~50 records in a single call to the server's DB. It will save expansive future API calls (expansive in term of both, server load and latency ).
    • On the View's aspect - I don't mind rendering 2-3 List items in each batch, if that will reduce the Frame's "window width".
    Is there any configuration that can achieve that?

  5. #5
    Sencha User Jamie Avins's Avatar
    Join Date
    Mar 2007
    Location
    Redwood City, California
    Posts
    3,661
    Vote Rating
    20
    Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough Jamie Avins is a jewel in the rough

      0  

    Default


    Can you provide any details on your List configuration, options, how many records you are loading?

    Sencha Inc

    Jamie Avins

    @jamieavins

  6. #6
    Sencha User
    Join Date
    Sep 2012
    Posts
    104
    Vote Rating
    0
    rbahumi is on a distinguished road

      0  

    Default


    I have found that the main cause for this issue was manually manipulating the records in the Store's load() listener:


    In my particular use-case, each of the models contains a few fields that are retrieved from the server's DB, and others which based on these DB values, are set manually in the client. I see now that this 'local record manipulation' is happening during/after the list is being rendered, and causing this long 2.4sec Frame.
    Removing the Store's load() listener had significantly reduced by ~10 times the magnitude of the frame's window width.

    0.233sec frame.jpg