Posted on 24. September 2013

Asynchronous loading of JavaScript in CRM 2013

When CRM2011 UR 12 (POLARIS) introduced asynchronous loading of web resources, there were some unfortunate side effects caused by the load order in which the scripts were executed not being the same as defined by form customisations. If you had scripts that required a previous scripts to be loaded first before it could be executed, you would experience random script errors due to the unpredictable load order. You can read more about the issue in the MSDN forums thread and the blog post I wrote explaining the behaviour and workaround.

Since then, the behaviour has been reverted back to the pre-UR12 loading technique as of Dynamics CRM2011 UR15 but this post describes what you can expect from Dynamics CRM 2013's script loading mechanism.

Why Asynchronous?

One of the reasons for moving to an Asynchronous loading model for JavaScripts was to make the form feel more responsive. Rather than having to load all scripts first before code can start and the form can be rendered, the scripts are loaded in parallel (as far as the browser will let them) resulting in a faster load time compared to a sequential load. Code that has all of its dependencies loaded can also start running before all of the unrequired scripts have completed being downloaded. Libraries such as RequireJs handle the complexities that can result from interdependent scripts all loading in parallel and your code needing to know when its dependencies are loaded before executing.

CRM2013 scripts do not load in order

With Dynamics CRM 2013, scripts are now loaded asynchronously and although the onload event will be executed after all scripts are loaded, the loading of the individual scripts will happen in the order that they are received from the server and not the order that they are specified in the form properties. Of course how you load JavaScript in Html Web Resources is still up to you!

Debugging Scripts

One by-product of this dynamic loading of scripts is that you won't see your web resources in the list of scripts using the F12 debugger since the browser has no <SCRIPT> tag to get the URL from. The script will still be available for debugging; however it will appear as an anonymous script block rather than in the list of script sources against its web resource URL. The technique I describe in the 'New Project' SparkleXrm tutorial of using www.fiddler2.com to create a re-direct to a script on the disk will still work because the browser request the web resource by its URL, allowing fiddler to intercept and redirect. Caching behaviour is unchanged with the web resources being cached by the client until customisations are published and the cache version is changed. You can read more about this mechanism in my web resource caching blog post on the subject.

Fast and Responsive

The good news is that as a result of the changes – forms are noticeable faster to load.

@ScottDurow

Posted on 29. March 2013

Asynchronous loading of JavaScript Web Resources after U12/POLARIS

UPDATE: This article is outdated - please refer to https://www.develop1.net/public/post/CRM-2013-Script-Loading-Deep-Dive.aspx 

We all know that UR12/POLARIS was a monumental release for Dynamics CRM what with the new Process Forms and Cross Browser support, but also included were some performance optimisations. One such improvement was a change to the way JavaScript Web Resources are loaded on forms so that they load and execute asynchronously rather than in the order that they were added to the form. The drawback with this optimisation is that it can cause the '..is undefined' script error if you have scripts that depend on other scripts being loaded first.

This post describes the loading behaviour and some possible solutions.

How did it work before UR12/POLARIS?

In my example I have 3 scripts, each dependant on the last. Mscorlib.js is the Script# system library that is needed before any other libraries can be loaded. I'm not talking about code that runs in the 'onload' event of a form but global code that is run after the script has downloaded used to define the prototypes of the objects that are used by the onload code.

In the Client.js, I might have some Script# generated code that requires a core Script# (ss.IEnumerable) type that is defined in a different script file.

Xrm.Sdk.DataCollectionOfEntity.registerClass('Xrm.Sdk.DataCollectionOfEntity', null, ss.IEnumerable);

For this code to run, the mscorlib.js must be loaded and executed first. The same applies if you are using jQuery and jQuery-UI.

The form definition used to have each script added in order so that they would be added to the 'head' section of the page as follows:

<head>
..
<script src=”/%7B635001685810000000%7D/WebResources/fdocs_/js/mscorlib.js” type=”text/javascript”></script>
<script src= /%7B635001685810000000%7D/WebResources/fdocs_/js/Xrm.js” type=”text/javascript”></script>
<script src=” /%7B635001685810000000%7D/WebResources/fdocs_/js/Client.js” type=”text/javascript”></script>
..

The resulting load pattern would be something like:

  • The script load/execution would be approximately:

    1. Mscrolib.js downloads and then executes
    2. Xrm.jsd downloads and then executes after mscrolib.js has completed executing since it follows it in the <HEAD> section of the page
    3. Client.js downloads and then executes after Xrm.js has completed executing since again it is added to the <HEAD> section in this order.
    4. The On Load event code then runs when all the scripts have loaded and finished executing

     

     

    What changes with the UR12/POLARIS update?

    Scripts are no longer in the <HEAD> section but are loaded asynchronously via the 'loadScriptAdv' function:

    loadScriptAdv('\x2f\x257B635001707050003339\x257D\x2fWebResources\x2ffdocs_\x2fjs\x2fmscorlib.js', '\x2f\x257B635001707050003339\x257D\x2fWebResources\x2ffdocs_\x2fjs\x2fmscorlib.js', false);
    loadScriptAdv('\x2f\x257B635001707050003339\x257D\x2fWebResources\x2ffdocs_\x2fjs\x2fXrm.js', '\x2f\x257B635001707050003339\x257D\x2fWebResources\x2ffdocs_\x2fjs\x2fXrm.js', false);
    loadScriptAdv('\x2f\x257B635001707050003339\x257D\x2fWebResources\x2ffdocs_\x2fjs\x2fClient.js', '\x2f\x257B635001707050003339\x257D\x2fWebResources\x2ffdocs_\x2fjs\x2fClient.js', false);
    

    The new asynchronous load behaviour results in the OnLoad being run after a smaller wait time since each script can execute without waiting for the preceding ones. This is a good performance gain but causes scripts to fail if the dependant scripts are not loaded at the point of execution.

  • The interesting thing is that this wasn't immediately apparent or was only an intermittent problem because once the scripts are loaded into the browser cache, the execution would usually be in the correct order. I can consistenly reproduce the issue by clearing down the browser cache and then disabling all caching.

    Ribbon JavaScript

    It's been a common technique to add dependant libraries to Ribbon Commands by adding them as Command Actions with a function of 'isNaN'

    <Actions>
    <JavaScriptFunction Library=”$webresource:mscorlib_crm.js” FunctionName=”isNaN”/>
    <JavaScriptFunction Library=”$webresource:xrm.js” FunctionName=”isNaN”/>
    <JavaScriptFunction Library=”$webresource:RibbonCommands.js” FunctionName=”someCommand”/>
    </Actions>
    
    

    It seems that the same issue exists with these libraries since they are loaded in an asynchronous fashion.

    Solutions

    Unfortunately, any solution that uses depending scripts in this way has to be updated – and like with all things, the right solution depends on your specific case.

    1) All script in a single library

    By far the simplest solution is to put all of your scripts into a single file in the correct order.

    I didn't' settle for single script option for the following reasons:

    1. A single script is harder to maintain
    2. Common libraries can't be shared between solutions
    3. All code must be downloaded on first use, rather than only downloading what is required at the time.

    2) RequireJs or HeadJs

    There are some good JavaScript loading libraries out there such as RequiresJs or HeadJs. These libraries allow you to dynamically load dependant script before you execute code that needs them.
    Gayan has an example of this technique on his blog. Maarten also has a good tutorial on his blog.

    I didn't settle for the RequireJs/HeadJs option for the following reasons:

    1. It requires you to manually set the Cache key to ensure that scripts are not downloaded every time they are needed
    2. It completely bi-passes the Dynamics CRM script loading mechanism in an 'unsupported' way
    3. There is some possibility that a backward compatibility option may be added into a future release (I'm ever the optimist!). Adopting this approach would make it harder to revert back to the standard script registration approach.

    3) Manually wait for required libraries to load

    The approach I took was to build a simple wait function to wrap code in that prevented execution until the required scripts had loaded. Each time a script completes, it adds its name to a semaphore array that is waited on by other libraries. The result is much the same as before UR12:

  • The difference here is that the scripts start executing as soon as they are loaded, but then wait using setTimeout until the required scripts have completed. The code will work on pre-UR12 systems as well, but because the wait is unnecessary, the code will simply execute the script without checking dependancies. setTimeout is used to release control to other scripts because Javascript is single-threaded.

    The Code

    Each script that has dependencies must be wrapped in the following:

    waitForScripts("client",["mscorlib", "xrm",],
    function () {
    //Original Code goes here
    });
    

    The first parameter provides the name of the current script, and the array is the list of scripts that must be loaded first.

    So in the Xrm.js library, you would wrap it in:

    waitForScripts("xrm",["mscorlib"],
    function () {
    //Original Code goes here
    });
    

    Each script must also include the waitForScript function at the bottom:

    function waitForScripts(name, scriptNames, callback) {
        var hasLoaded = false;
        window._loadedScripts = window._loadedScripts || [];
        function checkScripts() {
            var allLoaded = true;
            for (var i = 0; i < scriptNames.length; i++) {
                var hasLoaded = true;
                var script = scriptNames[i];
                switch (script) {
                    case "mscorlib":
                        hasLoaded = typeof (window.ss) != "undefined";
                        break;
                    case "jquery":
                        hasLoaded = typeof (window.jQuery) != "undefined";
                        break;
                    default:
                        hasLoaded = window._loadedScripts[script];
                        break;
                }
                allLoaded = allLoaded && hasLoaded;
                if (!allLoaded) {
                    setTimeout(checkScripts, 10);
                    break;
                }
            }
            if (allLoaded) {
                callback();
                window._loadedScripts[name] = true;
            }
        }
        // Only check for async loading of scripts if later than UR12/POLARIS
        if (typeof(APPLICATION_FULL_VERSION)!='undefined' && parseFloat(APPLICATION_FULL_VERSION.replace('5.0.',''))>9690.2835) {
    	setTimeout(checkScripts, 0);
        }
        else {
    	callback();
            window._loadedScripts[name] = true;
        }
    }

    Script# 'Script.template'

    I almost exclusively use Script# in my Dynamics CRM projects. What made this solution really work well for me was that I just include the code in the Script.template file to wrap the '#include[as-is] "%code%"'. The beauty is that I can now forget it's there and it just gets minified along with the rest of the code when deployed.

    This issue has affected a number of people I know, and I'm sure it'll start to become more of an issue as people start to upgrade to the latest Rollup. If you have any suggestions/comments, please let me know.

    The code was originally posted by me in the MSDN forums:

     http://social.msdn.microsoft.com/Forums/en-US/crmdevelopment/thread/fdca4779-e866-4e51-bab9-97a159f9cd37

     

    UPDATE: This issue is now fixed in UR15 -http://support.microsoft.com/kb/2843571

    @ScottDurow

    Posted on 21. February 2013

    Corrupt images after installing UR12 OnPrem

    I had a strange one today after upgrading one customer's Dynamics CRM servers to UR12.

    All was well apart from there being a few places that the images look corrupt.

    We tracked the cause down to an image that had not be replaced during the upgrade at the following location:

    C:\Program Files\Microsoft Dynamics CRM\CRMWeb\_imgs\imagestrips\grid_ctrl_imgs.png

    The pre-UR12 image was:

    The new UR12 image should be:

    Because this is an image strip, and the images had moved around, the css was pointing to the wrong place – and hence the corrupted images.

    Simply replacing the old image with the new one fixed the problem (after a browser cache refresh).

    If you have the same problem, you can download the updated image from here:

    grid_ctrl_imgs.png (5.46 kb)