Search code examples
javascriptperformanceunderscore.jsdeferred-execution

Underscorejs 'defer' (or setTimeout 1) not working consistently


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?


Solution

  • 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.