Web Performance Optimization: Asynchronous Script Loading

Today I am going to continue my week of Web Performance Optimization articles with bit of an advanced technique to asynchronously load script files. This technique is important for many reasons and can be used in a variety of ways as well. It solves several problems that can cause web applications to feel sluggish when they load.

We know the script tag with a type of text/javascript, which is the default, cause the browser to stop all other processing to load, evaluate and execute the script. Blocking is bad when you are trying to get your app to a usable state for the user. Using a bootstrapper, which is what I am going to demonstrate today is a way to not only reduce the number of script blocks that need to be evaluated when a web app is loaded, it also reduces the number of script files that need to be loaded initially, solving another performance issue.

Another performance issue this technique can solve is a Single Point of Failure or SPOF. This is where a script fails to load and causes the browser to lock up waiting for the script. I am sure we have all experienced this more often than we would like. Typically they are caused by tracking or advertising services that are offline, etc.

First, you need to examine your application and think about scripts that may not be needed when your application first loads. I have actually found this to be a good exercise just to evaluate my application's architecture even if I don't find a script to lazily load. When you identify these scripts you should consider a technique like this to load them.

The following is a reusable function that takes 3 parameters; id, url and callback. The id parameter is used to indentify the script block. This is important because it can be used to detect the script's presence in the page. If the script is already present it does not insert the script block again because this would cause more performance issues.

Duplicate scripts are a very serious problem on the web. Surveying sites I visit I routinely find problems with duplicate script references. You would be surprised how many times I see not only multiple jQuery references, but to different versions. This means not only does the browser need to download the script multiple times (assuming the script was not cached or the source url are different of course) it has to evaluate and execute the script. All this takes time even if it is a few hundred milliseconds here and there.

Another problem duplicate scripts introduce is overwriting code. Remember I said I see sites referencing more than one version of jQuery? That could actually cause issues and often does.

A Reusable BootStrapper Function

Of course using the id implies a certain level of discipline to ensure the developer uses the same id for the same script. I supposed you could write a routine that loops through all the script tags looking for a duplicate id, but at some point you have to assume the developer has a certain level of competency. I mean they are using a bootstrapper after all.

function loadScript(id, url, callback) {     if(!document.getElementById(id)) {      var script = document.createElement("script");      script.type = "text/javascript";      script.id = id;      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.body.appendChild(script);     } else {      callback();     }  }

?Let's look at the guts of the function now. After it checks for an existing script tag it creates a new script element. By the way, notice I am not using jQuery, but rather raw JavaScript. I can't assume jQuery is loaded.

After the script tag is create the type is set to 'text/javascript' and I know that is the default for most browsers now, but it does not hurt to do this here. Then the id is set to the id parameter value.

Next, if the browser is in a fully loaded state, think jQuery.ready(), it binds a method to the elements onload method that will execute. This means you can pass in a method that will get called once the script is actually loaded in the browser. I think the callback could be used to kick off your script's execution, like serve an add.

The last couple of lines set the script block's src to the url and then appends the element to the body tag.

Since creating this function earlier this summer I have come to learn that simply adding the script tag to the body tag may not always be effective. A technique that ultimately should work 100% of the time is to select the firs script tag and insert the new script before that tag.

var s = document.getElementByTagName("script")[0];s.parentNode.insertBefore(script, s);

Use the document.getElementByTagName('script') to get an array of script nodes. Reference the first node and it's parentNode. Then insert the new script tag before the first script tag. First there should be at least one script block because the boot loader has to be in a script node of course. Referencing the first script block's parentNode will reference either the document's HEAD or BODY tags. Believe it or not a document may not have a HEAD tag so you can't reliably reference the HEAD tag. So either way you will get a reference to the BODY or HEAD tags (most likely) and insert the new script block there.

Self Executing Function

Another technique is to use a self executing anonymous function instead of the callback mechanism. Facebook's Like plugin actually uses this technique.

<div id="fb-root"></div><script>(function(d, s, id) {  var js, fjs = d.getElementsByTagName(s)[0];  if (d.getElementById(id)) return;  js = d.createElement(s); js.id = id;  js.src = "//connect.facebook.net/en_US/all.js#xfbml=1";  fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));</script>

All the other points I made above are covered in the example I shared above. The self-executing function is not as reusable as the previous function example.

Facebook Like Code

Conclusion

Using a script loader bootstrapper or a self-executing function to asynchronously load scripts solves several performance related issues common to many web sites. They help fight duplicate scripts, reduces the number of scripts loaded when the app is initially loaded and potentially reduce the chance of a Single Point of Failure. So example your web applications to determine if they could benefit from asynchronously loading scripts.

Share This Article With Your Friends!

Googles Ads Facebook Pixel Bing Pixel LinkedIn Pixel