Dynamically Loading Javascript Synchronously

Today I have been working on a new security challenge that meant that I had to load a number of javascript sources dynamically. Now there are multiple references to be found on Google on how to do this, however the challenge that I faced was the ability to load scripts in a specific order. This is unfortunately more difficult that it seems due to browser optimisation… let me give you an example.

In the case of jQuery and jQuery-UI, the order in which the scripts are loaded is important. This means that when developing a webpage, you would usually do something to the effect of:



Therefore forcing the browser to load each script sequentially. The problem however is presented when dynamically loading javascript using a method similar to:

var loadCount = 0;
var scripts = [
"http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js",
"http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js",
];

function loadJavascript(url) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;

script.onreadystatechange = function() {
if (this.readyState == 'complete')
handler();
}

script.onload = handler;

if (typeof script != "undefined") {
document.getElementsByTagName("head")[0].appendChild(script);
}

return script;
}

function handler() {
loadCount++;
}

for(x = 0; x < scripts.length; x++) {
loadJavascript(scripts[loadCount]);
}

On first appearance, this script seems to work correctly. We do however encounter a problem when working with jQuery due to browser optimisations, as can be seen from this timeline screenshot:

You can see that both scripts are being downloaded simultaneously by the browser. This means that in the case of jQuery, every so often (usually determined by the file size, and speed of response from the server) you will see an error such as the follows:

The solution to this I have found lies in recursion. By taking our script above and making a slight modification, we can synchronously download each javascript file.

var loadCount = 0;
var scripts = [
"http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js",
"http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js",
];

function loadJavascript(url) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;

script.onreadystatechange = function() {
if (this.readyState == 'complete')
handler();
}

script.onload = handler;

if (typeof script != "undefined") {
document.getElementsByTagName("head")[0].appendChild(script);
}

return script;
}

function handler() {
loadCount++;

// Use recursion here to load the next script when we know our last is successfully loaded
if (loadCount < scripts.length)
loadJavascript(scripts[loadCount]);
}


loadJavascript(scripts[loadCount]);

Now if we look at a timeline of our script loading, we can see that our browser is waiting for one script to finish downloading before progressing to the next.

Whilst this technique obviously has a performance impact when used for anything in a production environment, I have found it to be invaluable when dealing with dynamic loading of scripts such as jQuery.

Advertisements