Dynamic Script Loading described in High Performance JavaScript, is the most frequently used pattern for non-blocking JavaScript downloads due to its cross-browser capability and ease of use. The main idea is to dynamically load JavaScript after page is loaded. The concise code would be like this,

function loadScript(url, callback){
	var script = document.createElement("script");
	script.type = "text/javascript";
	if (script.readyState){ //IE
		script.onreadystatechange = function(){
			if (script.readyState == "loaded" || script.readyState == "complete"){
				script.onreadystatechange = null;
				callback();
			}
		};
	} else { //Others
		script.onload = function(){
			callback();
		};
	}
	script.src = url;
	document.getElementsByTagName("head")[0].appendChild(script);
}

imageNevertheless, I don’t want to discuss the function downloading JavaScript dynamically. Instead, I would like to talk about more about how to guarantee the order of downloading scripts. As you may know, of all the major browsers, only Firefox and Opera guarantee that the order of script execution will remain the same as you specify. Other browsers will download and execute the various code files in the order in which they are returned from the server. You can guarantee the order by chaining the downloads together, such as:

loadScript("file1.js", function(){
	loadScript("file2.js", function(){
		loadScript("file3.js", function(){
			alert("All files are loaded!");
		});
	});
});

However, the book suggest that If the order of multiple files is important, the preferred approach is to concatenate the files into a single file where each part is in the correct order. That single file can then be downloaded to retrieve all of the code at once (since this is happening asynchronously, there’s no penalty for having a larger file).

The suggestion is reasonable in most cases though, there are still a handful of occasions we can hardly have the control of the scripts, say using a CDN resources, a pure client application without server code, etc. And for these occasions, if the order is also of paramount importance, I bet we should work out a better solution.

Here’s my thought. The function loadScript is pretty good and clear. I don’t think there is anything that I should put effort on the function itself. Rather than the function, I am a bit curious about the parameters.

The first parameter required is the url of script to download. Hmm... I can hardly figure out any improvement on it. And the second parameter is the callback function, well, this looks promising because you are the lord of this function and its definition is extremely flexible. I bet this parameter would be my breakthrough. My first try of the code looks like this,

function loadScriptByOrder(urls){
	var loadFun = function(){
		alert('All files are loaded!');
	};	
	for(i = urls.length - 1; i >= 0; i--){
		// create closure for each callback
		loadFun = function(){
			loadScript(urls[i], loadFun);
		};
	}
	loadFun();
}

// the only parameter is an array of js file url
loadScriptByOrder(['file1.js', 'file2.js']);

The function didn’t work as expected. A bloody stack-over-flow exception occurred. What’s wrong with it? The error is not straightforward to debug. But, wait, do you think the code in for loop is somehow weird? Aha, we literally overlooked our tricky buddy, you guessed it, closure. Then I changed the code a little bit to ensure every callback has their own closure like below, and it works like a charm.

loadFun = (function(url, callback){
	return function(){
		loadScript(url, callback);
	};
})(urls[i], loadFun);

To warp up, Dynamic Script Loading is not difficult to apply, instead, it’s wildly used in every single corner of today’s web applications. This post is written in the half-way of reading the book, High Performance JavaScript. Aiming to provide a elegant way to load JavaScript by order.

 posted on 2011-02-24 17:39  助平君  阅读(1403)  评论(0编辑  收藏  举报