views:

502

answers:

4

I have the following (javascript/jquery) code to show a busy indicator (after a delay) while an image is loading:

function imgUpdate(arg) {
    var loaded = false;

    $("#image").one("load", function(){
        loaded = true;
        $("#busyIndicator").hide();
    });

    setTimeout(function(){
        if (!loaded) {
            $("#busyIndicator").show();
        }
    }, 250);

    $("#image")[0].src = arg;
}

Sometimes, the indicator comes up and stays up. How is this possible if the browser's javascript engine is single-threaded? (This is on Firefox 3, by the way.)

One note: this seems to happen when the image being loaded is already cached.

Another note: if I log to my firebug console, all of the lines in imgUpdate are executed, but a log message inside the onload handler never prints on subsequent calls to imgUpdate.

+1  A: 

I'm hard pressed to replicate this.

Here is the implementation of what you're doing:

A version using caching: http://jsbin.com/uwuho

A version with caching being prevented: (uses parameter to avoid caching) http://jsbin.com/oguvi

Hit F5/Ctrl-F5 to see it go. (in particular with the version which prevents caching)

With or without caching neither version is doing what you'd described.

Your problem probably lies elsewhere.

altCognito
I couldn't replicate it using your code, either. But if the problem were something else, the fix I posted (in an answer here) doesn't seem like it would have worked.
Jim Hunziker
+1  A: 

Is there any other javascript on the page that breaks? If so, this may not be a race condition -- JS could simply stop executing before the busyIndicator is hidden again...

Matt Newby
Nope - firebug shows that there aren't any errors and that an unrelated ajax request is still executing.
Jim Hunziker
A: 

You might want to clear the timeout in your callback so that it won't fire if the image is loaded.

var timer = null;
function imgUpdate(arg) {
    var loaded = false;
    timer = setTimeout(function(){ 
        $("#busyIndicator").show();
        timer = null;
    }, 250);        
    $("#image").one("load", function(){
        if (timer) {
            clearTimeout(timer);
            timer = null;
        }
        $("#busyIndicator").hide();
    });
    $("#image")[0].src = arg;
}
tvanfosson
I tried this, but it still exhibits the same problem. I updated the last line of my question with some additional info.
Jim Hunziker
+1  A: 

Clearing the image's src tag seems to fix the problem:

function imgUpdate(arg) {
    var loaded = false;

    $("#image").one("load", function(){
        loaded = true;
        $("#busyIndicator").hide();
    });

    setTimeout(function(){
        if (!loaded) {
            $("#busyIndicator").show();
        }
    }, 250);

    $("#image")[0].src = "";
    $("#image")[0].src = arg;
}
Jim Hunziker