1

Ready to face the backlash as I'm aware that this is an oft-asked question here, and fully aware that there are "much better" ways of accomplishing what I want to achieve...

I have a long running JavaScript operation in a function which can take up to 10 seconds to run. I have a "loading div" that I can show and hide at will. Normally this show/hide operation works very quickly but if I attempt to do so in the same call chain as my long running function, the browser does not get chance to reflow and show the loading div before it is locked up by the long running function.

I have tried:

1 - just showing the div then calling the function:

$('#loadingDiv').show();
longRunningFunction();

2 - showing the div then calling the long running function inside a setTimeout to be executed after the current function has finished:

$('#loadingDiv').show();
setTimeout(function(){longRunningFunction();}, 0);

3 - same as 2 but with a longer timeout:

$('#loadingDiv').show();
setTimeout(function(){longRunningFunction();}, 100);

With an even longer timeout (say, 5000 milliseconds) I am able to get the loading screen to show and my long running function to execute properly - but obviously this adds to the execution time and I guess won't be cross-browser compatible (it just so happens that the browser I am using happens to do a repaint in those XXXX milliseconds - another browser may not?)

I've also tried a mish mash of tricks I've seen on SO and elsewhere to do with changing the offsetLeft / offsetTop on divs on the page, changing self.status, but everything I do seems to have the same effect: lock up the browser for a few seconds, with the old content on the page, then show the loading div once the long running function has finished.

I know what I'm doing is "wrong" but it is what I want to do for the moment - ultimately I will:
1) Break this code down into smaller chunks
2) Potentially move it into a Web Worker
3) Speed it up so that it does not take so long and doesn't lock up the browser

But for now can anyone suggest an easy way of forcing that "loading div" to be shown to the user before my long running operation runs?

Jim L
  • 476
  • 4
  • 15
  • 3
    the setTimeout should have been more than enough. I suspect something more is going on. – Kevin B Dec 02 '13 at 19:09
  • Even if you manage to show the element, a JavaScript function eating up the CPU for that long is going to make the page very sluggish. It's likely that at least some browsers will complain with a "Script Gone Crazy" popup. – Pointy Dec 02 '13 at 19:10
  • What about: `$('#loadingDiv').show(0,longRunningFunction);` ??? Even i don't think it will have different behaviour than using show() – A. Wolff Dec 02 '13 at 19:12
  • Also, surely things work if you use a timeout value of `5000` instead of `100`, right? If not, then it's likely that something else is going wrong in your code. – Pointy Dec 02 '13 at 19:12
  • setTimeout is limited to 4ms .. you should use a callback when things are ready. – rafaelcastrocouto Dec 02 '13 at 19:14
  • @Pointy - agreed - though this is just for a demo and further down I will of course do it properly. FWIW I've found that the browser isn't actually sluggish once it's come "back to life" - but yes occasionally FireFox does complain - demoing in Chrome! ;-) – Jim L Dec 02 '13 at 19:14
  • There are certain properties you can request that will force a reflow (because those property values would be inaccurate without first doing any pending reflows). You can read about that here: [Which DOM element properties can cause the browser to perform a reflow operation when modified?](http://stackoverflow.com/questions/11616619/which-dom-element-properties-can-cause-the-browser-to-perform-a-reflow-operation/11616742#11616742) – jfriend00 Dec 02 '13 at 19:18
  • @Pointy - yes increasing the timeout value to a bigger number does have the desired effect - albeit obviously adding to the execution time. Have added that info to the question - cheers – Jim L Dec 02 '13 at 22:35
  • Try adding $('#loadingDiv').height(); right after .show() as checking width/height of an element forces a reflow. – pstenstrm Dec 02 '13 at 22:41

3 Answers3

1

Use a callback:

$('#loadingDiv').show("fast", longRunningFunction);

This will call your function after the div is shown.

ramblinjan
  • 6,578
  • 3
  • 30
  • 38
0

I would possibly suggest looking into using jquery's deferred functionality for this purpose. Deferred objects where made for this and you can find a tutorial regarding how to use this at http://net.tutsplus.com/tutorials/javascript-ajax/wrangle-async-tasks-with-jquery-promises/

I've created a sample fiddle which shows you how it's used. http://jsfiddle.net/Nemesis02/PH2nE/1/

Here's how the JavaScript is written

(function($) {
    function initLongWait() {
        var deferred = new $.Deferred();

        setTimeout(function() {
            deferred.resolve();
        }, 2000);

        return deferred.promise();
    }

    var promise = initLongWait();
    $('#wait').fadeIn();
    initLongWait().done(function() {
        $('#wait').fadeOut();
        $('#done').fadeIn();
    });
})(jQuery);
Nemesis02
  • 292
  • 2
  • 9
0

jsFiddle Demo

You can take the approach you used in #2 and it should work.

$("#loadingDiv").show();
setTimeout(function(){
 longRunningFunction();
 $("#loadingDiv").hide();
},10);
Travis J
  • 81,153
  • 41
  • 202
  • 273