2

In a large-scale JavaScript application I have a similar case like this:

var $box = $('#box');
var expensiveOperation = function () {
    for (var i = 0; i < 10000; i++) {
        for (var j = 0; j < 4500; j++) {
            Math.random();
        }
    }
};

$('#show').click(function () {
    $box.show();
    expensiveOperation();
});

$('#showDefer').click(function () {
    $box.show();
    _.defer(expensiveOperation);
});

$('#hide').click(function () {
    $box.hide();
    expensiveOperation();
});
$('#hideDefer').click(function () {
    $box.hide();
    _.defer(expensiveOperation);
});
#box {
    background-color:red;
    width:100px;
    height:100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="box"></div>
<button id="show">show</button>
<button id="showDefer">show defer</button>
<button id="hide">hide</button>
<button id="hideDefer">hide defer</button>

jsFiddle link, just in case: http://jsfiddle.net/oymaterz/5/

I want to either hide or show a DOM element and the perform an expensive operation. For performance reasons, I want to always ensure that the show/hide are executed first (that is, at the top of the execution stack). This is demonstrated in the example I provided (using underscore's defer) and its working fine under the latest version of Chrome. Also, the above example doesn't work on IE11. Hide/show defer its still slow.

However, when I do the same in my application it works only intermittently and strangely IE11 seems to consistently work fine .

Any ideas as to why I get this behaviour?

gen_Eric
  • 223,194
  • 41
  • 299
  • 337
skay-
  • 1,546
  • 3
  • 16
  • 26
  • Your example seems to work just fine for me in IE 11 (Windows 7). – gen_Eric Dec 29 '14 at 16:48
  • So both hide and show defer are executed immetiedly? Mine is win7 ie 11 as well.. – skay- Dec 29 '14 at 17:01
  • 2
    I think I see the problem. The browser still needs to *run* the `expensiveOperation` at some point. It looks that in IE, while it's doing that, the UI is locked (JavaScript is single threaded), so need to wait before you can click another button. – gen_Eric Dec 29 '14 at 17:07
  • Hmm what i actually mean by its not working correctly is that if you click hide, it will first execute the `$box.hide();` and then the `expensiveOperation` but because of the browser's rendering engine (?) the div will actually get hidden (even though that its state was changed to hidden before) after the `expensiveOperation`. Btw... in the latest Firefox (on OSX) it doesn't work... (increase the second for to something like 19500 if you want to check). – skay- Dec 29 '14 at 17:26
  • I'm not 100% sure what's going on, but it's probably due to the difference between Chrome and IE's JavaScript engines (IE's is slower). – gen_Eric Dec 29 '14 at 17:27

1 Answers1

1

I found a solution to my problem. Although it is expected that _.defer (of setTimeout 1) should have worked... it wasn't very consistent in my case. So I ended up using the requestAnimationFrame method. Heres the updated version:

var expensiveOperation = function () {
    for (var i = 0; i < 10000; i++) {
        for (var j = 0; j < 4500; j++) {
            Math.random();
        }
    }
};


var $box1 = $('#box-1');


$('#show-1').click(function () {
    $box1.show();
    expensiveOperation();
});

$('#showDefer').click(function () {
    $box1.show();
    _.defer(expensiveOperation);
});

$('#hide-1').click(function () {
    $box1.hide();
    expensiveOperation();
});

$('#hideDefer').click(function () {
    $box1.hide();
    _.defer(expensiveOperation);
});

var $box2 = $('#box-2');

$('#show-2').click(function () {
    $box2.show();
    expensiveOperation();
});

$('#showRequestAnimationFrame').click(function () {
    var flag = true;
    requestAnimationFrame(function showAnimFrame(){
        if(flag) {
            $box2.show();
            flag = false;
            requestAnimationFrame(showAnimFrame);
        } else expensiveOperation();
    });
});

$('#hide-2').click(function () {
    $box2.hide();
    expensiveOperation();
});

$('#hideRequestAnimationFrame').click(function () {
     var flag = true;
    requestAnimationFrame(function showAnimFrame(){
        if(flag) {
            $box2.hide();
            flag = false;
            requestAnimationFrame(showAnimFrame);
        } else expensiveOperation();
    });
});
#box-1 {
    background-color:red;
    width:100px;
    height:100px;
}

#box-2 {
    background-color:blue;
    width:100px;
    height:100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="box-1"></div>
<button id="show-1">show</button>
<button id="showDefer">show defer</button>
<button id="hide-1">hide</button>
<button id="hideDefer">hide defer</button>

<p></p>

<div id="box-2"></div>
<button id="show-2">show</button>
<button id="showRequestAnimationFrame">show requestAnimationFrame</button>
<button id="hide-2">hide</button>
<button id="hideRequestAnimationFrame">hide requestAnimationFrame</button>

jsFiddle link, just in case: http://jsfiddle.net/oymaterz/6/

It is worth mentioning that the requestAnimationFrame is not supported in IE8. If you want support for that I will suggest use Paul Irish's excellent GIST for cross-browser compatibility.

I hope that this will help someone in similar situation as me. It clearly works fine for me now and it proofed to be a life-saver.

skay-
  • 1,546
  • 3
  • 16
  • 26