Jim L
Jim L

Reputation: 486

Is there a way to force a reflow before a long-running JavaScript operation?

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?

Upvotes: 1

Views: 1262

Answers (3)

Travis J
Travis J

Reputation: 82287

jsFiddle Demo

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

$("#loadingDiv").show();
setTimeout(function(){
 longRunningFunction();
 $("#loadingDiv").hide();
},10);

Upvotes: 0

ramblinjan
ramblinjan

Reputation: 6694

Use a callback:

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

This will call your function after the div is shown.

Upvotes: 1

Nemesis02
Nemesis02

Reputation: 292

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);

Upvotes: 0

Related Questions