Everyone in the Microsoft Ajax team has a favorite feature, the CDN, jQuery Integration, and mine happens to be the Script Loader. I’m fond of it because it has some really cool features that aid the performance of my web application and take away much of the headache associated with organizing and loading any scripts that I use either from Microsoft, my own application or indeed a third party library like jQuery. The headache I’m referring to can be best characterized in the example below where I am loading scripts needed to use controls from the Ajax Control Toolkit (ACT) and the various components of the Microsoft Ajax Library. There are multiple scripts required and prior to the Script Loader I would be bringing them in all manually and having to worry myself with loading them in the right order. Not fun.
- // Microsoft Ajax Library scripts
- <script type="text/javascript" src="../Scripts/MicrosoftAjax/MicrosoftAjaxCore.js"></script>
- <script type="text/javascript" src="../Scripts/MicrosoftAjax/MicrosoftAjaxComponentModel.js"></script>
- <script type="text/javascript" src="../Scripts/MicrosoftAjax/MicrosoftAjaxSerialization.js"></script>
- <script type="text/javascript" src="../Scripts/MicrosoftAjax/MicrosoftAjaxGlobalization.js"></script>
- <script type="text/javascript" src="../Scripts/MicrosoftAjax/MicrosoftAjaxTemplates.js"></script>
-
- // ACT Controls
- <script src="../Scripts/ACT/ACTRegisterExtended.js" type="text/javascript"></script>
- <script src="../Scripts/ACT/ACTWatermark.js" type="text/javascript"></script>
- <script src="../Scripts/ACT/ACTExtenderBase.js" type="text/javascript"></script>
- <script src="../Scripts/ACT/ACTCommon.js" type="text/javascript"></script>
This can all be made better – let’s find out how by starting at the beginning with start.js.
Getting started with the script loader
Start.js is your friend. It contains the Script Loader which is super small and knows about all the scripts of the Microsoft Ajax Library, some of which are listed in the above sample. You include it just as you would any other script like so:
- <script type="text/javascript" src="../scripts/start.js"></script>
It’s also available via our brand spanking new, smoking fast Microsoft Ajax CDN so your customers will get blisteringly fast script downloads wherever they are in the world. ScottGu’s got more info on the CDN here.
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/beta/0910/Start.js"></script>
Now that you’ve got the Script Loader bootstrapped it’s time to “harness the power of the atom” (Scott Hanselman’s favorite saying at the moment) and start loading scripts into your application. The Script Loader allows you to load libraries and scripts into your application without having to worry about where the code file is. Now this is where it gets sexy (yes JavaScript can be sexy). In the following example I want to use the dataView control and the ApplicationServices script (both included in Microsoft Ajax Library) so I tell the Script Loader to get them ready for me using “Sys.require”:
- <script src="../Scripts/MicrosoftAjax/start.js" type="text/javascript"></script>
- <script type="text/javascript">
-
- // Load required scripts for DataView client control, using script Loader
- Sys.require([Sys.components.dataView, Sys.scripts.ApplicationServices], function () {
-
- // do some stuff with dataView and Application Services
-
- });
-
- </script>
Once the Script Loader fetches them (in parallel) and executes them it checks if the DOM is ready and then calls the function I’ve specified in the second parameter of Sys.require. On a side note, we can also bring in jQuery by adding Sys.scripts.jQuery to the array.
Let’s take it further by considering a common scenario where I have created a custom script for my application. The script is only needed when a user clicks a certain button though, so we’re going to register it with the Script Loader but only execute it on-demand when (this is an example of what we call “lazy loading”):
- <script src="../Scripts/MicrosoftAjax/start.js" type="text/javascript"></script>
- <script src="../Scripts/RegisterMyAppScript.js" type="text/javascript"></script>
-
- <script type="text/javascript">
-
- function useMyCustomScript() {
-
- Sys.require(Sys.scripts.myAppScript, function () {
- // Use my custom script
- });
-
- }
-
- </script>
-
- <input type="button" onclick="useMyCustomScript()" value="Click me"/>
“But wait”, I can hear you say, “what is that extra script we reference – “RegisterMyAppScript.js”? Good question – that’s where we define our script and any other scripts that it depends on.
Let’s be definitive
Within RegisterMyAppScript.js we use the Script Loader’s “defineScripts” method to make it aware of the location of our script and any dependencies that it may have on other scripts:
- Sys.loader.defineScripts({
- releaseUrl: "%/../CustomScripts/{0}.js",
- debugUrl: "%/../CustomScripts/{0}.js"
- },
- [
- { name: "myAppScript",
- executionDependencies: ["ApplicationServices"],
- isLoaded: !!(window.My && My.Scripts && My.Scripts.TheScript)
- }
- ]
- );
First we specify a release and debug URL and depending on how we’ve setup the loader it will use a minified version or a version with comments and intellisense (we’ll get into that later). Then we pass a parameter to defineScripts that provides the information about the different scripts we want to register – in our case “myAppScript”. For each script we specify the name (filename without the file extension) its execution dependencies and an isLoaded test that we use to check if the script has already been loaded (we don’t want to load it again!)
If this were a custom control we were loading then we could also tell the Script Loader under which namespace our behaviors are loaded.
Now we can let the Script Loader worry about the fact that my custom script requires ApplicationServices from the Microsoft Ajax library and it will load all the required scripts in parallel, maximizing performance whilst only executing them when they are ready.
Next we’ll look at what the actual custom script looks like.
Parallel Power with dependency management built in
In the past, loading scripts in sequence made sense when you had complex dependencies that tied the scripts together however this is not recommended practice if performance is important. Let us assume that both having modular code (dependencies) and good performance is important to you. Whilst browsers have been able to load two scripts in parallel for some time, modern browsers let you load many at once but this in itself can raise a couple of problems. Firstly, we have a “race condition” where Script A is racing against Script B to be loaded and executed. When there are dependencies between A and B and the dependent script loads first (for whatever reason) your code will break because it is expecting a symbol to be present that has yet to be loaded and executed by the browser. Secondly, only modern browsers allow us to load more than two scripts in parallel (there are some hacks you can do but let’s assume we’re not using them), so if we have a large collection of scripts with dependencies we will still be waiting an undesirable length of time.
The Script Loader allows us to download multiple scripts in parallel whilst managing all their dependencies and making sure that we only execute the scripts once a dependency is satisfied.
There’s one more question that needs answering though. How do we load the scripts, like RegisterMyAppScript.js, without executing them automatically? We solve this problem by wrapping the script in an execute function and then check to see if the Script Loader is ready (loaded). If it’s ready to rock then we then call registerScript, passing in the name of the component and the execute callback function. Now the loader knows that this is the script that must be called when all its dependencies are in place – and that execute is the function to make this happen.
- (function() {
-
- function execute() {
- // .. script content ..
- }
-
- if (window.Sys && Sys.loader) {
- Sys.loader.registerScript(“myAppScript”, null, execute);
- }
- else {
- execute();
- }
-
- })();
You’ll notice that if the Script Loader is not there (for whatever reason) then we just go ahead and execute the script anyway – with this we are preserving the original functionality of the script.
Power to the developer
A lot of developers are scared by JavaScript development. However, more recently with the introduction of libraries like Microsoft Ajax Library and jQuery as well as better intellisense support and browser debugging there is no longer much of a reason to hide behind the cushions. Remember earlier on where we specified a release and debug version of the scripts? The Script Loader has a property we can set to enable a debug mode which when we are running our scripts and stepping through them using our favorite browser plugins is really useful. When set to true the loader uses the version which is not minified and has comments so we can figure out where our script is going wrong.
Summary
That’s it for now but there is a bunch more features in the Script Loader that I will cover off in future blog posts, including:
- Lazy Loading
- Intellisense in Visual Studio
- How the loader does the ordering of scripts
- Handling DOM ready events etc
In this blog post we’ve seen how script loading and management of execution is made easier using the Script Loader from the Microsoft Ajax Library as well as covering off how we handle dependencies between different scripts and debugging.
To find out more about what’s in the Microsoft Ajax Preview 6 release check out the following resources and be sure to come to Microsoft PDC where we’ll be doing sessions on the new stuff!