Loading scripts in parallel causes bug in WebKit browsers

Hi,

I have a function called ‘loadScript’ which is used to load JavaScript files in parallel (for the purpose of not blocking the browser from downloading other components at the same time).

See the following link for more information on the subject:
http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/

This function works fine for IE and Firefox and up until today has always worked for Chrome/Safari. But today I noticed an issue for WebKit rendering engines, which is that sometimes the callback method I pass to the loadScript doesn’t always fire, only sometimes when I force refresh the page.

The code for the loadScript function is as follows…


var loadScript = function(url, callback)
{
	var script = document.createElement("script");
	script.type = "text/javascript";
	
	if (script.readyState) { // Internet Explorer
		script.onreadystatechange = function()
		{
			if (script.readyState == "loaded" || script.readyState == "complete") {
				/*
				 * Oddly the final readyState isn’t always "complete". 
				 * Sometimes, readyState stops at "loaded" without going on to "complete" 
				 * and sometimes it skips over "loaded" altogether. 
				 * 
				 * The best approach is to check for both readyState values 
				 * and remove the event handler in both cases to ensure you don’t handle the loading twice:
				 */
				script.onreadystatechange = null;
				callback();
			}
		};
	} else {
		script.onload = function()
		{
			callback();
		};
	}
	
	script.src = url;
	document.getElementsByTagName("body")[0].appendChild(script);
};

…and I call the function in my HTML page as follows…


<script src="Assets/Scripts/loadScript.js"></script>
<script>
	loadScript("Assets/Scripts/Library.js", function() {
		STORM.init();
	});
</script>

Any help would be really appreciated as this is confusing the hell out of me :slight_smile:

Kind regards,
M.

Are any javascript errors produced?

That’s the really annoying thing, there are no errors!

Sometimes it will run, sometimes it wont.

So frustrating having to work out what’s going on with something if it doesn’t have the decency to at least throw you an error somewhere along the line.

looks like others have had the same problem
[google]safari script onload[/google]

It might be that check for “script.readyState” that’s throwing it off:


if (script.readyState) {}

You’re not testing if the property exists, you’re just testing that it’s a “truthy” value. What happens if you completely get rid of that check?

Also, it might be worth peaking into the jQuery source:


script.onload = script.onreadystatechange = function(){
    if ( !done && (!this.readyState ||
            this.readyState === "loaded" || this.readyState === "complete") ) {
        done = true;
        success();
        complete();

        // Handle memory leak in IE
        script.onload = script.onreadystatechange = null;
        if ( head && script.parentNode ) {
            head.removeChild( script );
        }
    }
};

I haven’t been able to reproduce this issue with my script loader LABjs, which fundamentally uses very similar logic.

I suspect that maybe what is happening is that your script isn’t actually fully loading (for whatever reason) and that’s why the flakiness of the load handler not being called sometimes.

It might be good to inspect the dom and the page (with a console or whatever) when you have an occurence of the handler not firing, and see if the script in question really did load or not.

@JimmyP
Thanks for the suggestions, but if I remove that check for “readyState” then there would be no way to tell the difference between the browser implementations.

Looking at that jQuery code snippet you provided made partial sense, it looks like they have a ‘done’ variable to check when the script has loaded for WebKit/Gecko browsers and then also check it against the IE specific readyState object to make sure it returns the relevant values.

Problem is that I can’t work out where ‘done’ gets its value from in the jQuery code (simply because there is so much of it and keeping track of it is a bit of a nightmare).

@shadedecho
I check the page resources using Chrome/WebKit’s “resources” tab and it shows that both the scripts load fine? but for whatever reason the ‘onload’ event just doesn’t fire.

M.

Can you provide a test page that demonstrates (at least intermittently) this bug with simple code and no extras in the way? I’d like to be able to reproduce it, but I haven’t yet been able to.

Also, what exact version of Webkit are you testing with? Safari 4? What platform?

No problem i’ll look at getting this together for you today.

Thanks for your help!

M.

OK, I stand corrected.

Just as I was getting together an example, I discovered that the loadScript IS working fine and running the callback function, but the callback function then has a call to jQuery’s

$(document).ready()

and that method is the one that doesn’t seem to run every time.

I then went ahead and created a test page and found that once I had stripped out nearly all of the code (so we could concentrate on testing just the loadScript and jQuery Dom Ready) it turns out that no matter what I do (whether I load the page directly, link to it, refresh it while I’m on it) the Dom Ready event doesn’t fire!

In my example I’m trying to load the script “Library.php” and although it’s a php file it actually just concatenates the jQuery library and then my Js code and returns a single Js file. I’ve also tested it by calling a js file called “testing.js” which its source code was both the jQuery library again followed with my Js code (just in case it was the PHP causing a problem) - but this still gave the same results.

I’m on Windows XP and the specific Chrome/WebKit versions are as follows…

Safari 4.0.1 (531.21.10)
Chrome (4.0.288.1 dev)

Example files attached.

Any ideas or help greatly appreciated.

Thank you!

M.