Tags: debugAtAllCosts, dojo.require, loader, XDomain loader
On pages 19-20 of my book, you’ll notice a handy table of the various configuration switches you can pass to djConfig to customize aspects of the bootstrapping process. A sidebar I’d like to have included (and will include in a future printing or update) involves some additional explanation about how the debugAtAllCosts option works, and incidentally, explains some important nuances between the “normal” loader and the XDomain loader in the process.
At a glance, debugAtAllCosts is simply what you use to try and get some more specific debugging information when something has gone wrong and Firebug or Firebug Lite aren’t giving you much to work with. Oftentimes, a quick update to djConfig with debugAtAllCosts:true is all it takes to get that helpful information and isolate the issue.
But how does it do that?
Detour: A Primer on the Loaders
To better understand how debugAtAllCosts works, it’s critical to first understand the difference between the “normal” loader and the XDomain loader. (Recall that the XDomain loader works around the same origin policy and is used to bootstrap the toolkit from a domain that’s different from where your web page is located to bring your page to life with Dojo goodness e.g. AOL’s CDN and such things.) To illustrate, consider a simple HTML page that might have a script block in it that looks something like this:
In the general case when the “normal” loader is being used, each of those three dependencies is evaluated synchronously such that the scripts for A, B, and C are each fetched in turn, and any dependencies are processed in a depth-first search fashion. For example, if B had dojo.require’d modules named D and E (in that order), then the overall order of requests look like A -> B -> D -> B -> E -> B -> C. Note that B is listed multiple times in that chain because B begins loading, then at some point (maybe immediately) D is processed, then E is processed, and then control returns to B one last time before C gets kicked off.
It’s worth reiterating is that the execution of D and E are “blocking” in the sense that B begins executing, then when the dojo.require(“D”) statement is reached within B, D is processed on the spot before B finishes executing; ditto for the way that E works.
With the “normal” loader, the entire process is entirely predictable every single time. You can not only count on the dojo.require dependencies being resolved properly, but you also have an implicit guarantee about the relative order of various dojo.require statements that appear sequentially in the same source file as in the previous example.
The XDomain (cross-domain) loader, however, does something different by necessity of the way it circumvents the same origin policy, which can’t be done with dojo.xhrGet. It uses information provided through other djConfig switches such as baseUrl and modulePaths, or uses information provided via the dojo.registerModulePath function to build URLs that serve as the SRC attribute for dynamically insert SCRIPT tags that get inserted into the HEAD of the page. Moving forward, realize that the dynamic insertion of SCRIPT tags is an operation that is asynchronous in nature.
One of the most significant differences to note is that while dojo.require dependencies are still preserved by the XDomain loader, the relative order in which non-dependent modules become ready (where “ready” roughly corresponds to “alive” or “fully processed”) is not necessarily preserved. In our working example, this means that while the XDomain loader guarantees that D and E will be ready before B is ready, it guarantees nothing about whether D will be ready before E or if E will be ready before D — nor does it guarantee anything about the relative order of A, B, or C becoming ready. The reason why there are no such guarantees is simply due to the nature of asynchronous requests and latency.
To make sure you fully grasped the important nuances in that paragraph, here are a few of the possible scenarios with the XDomain loader for our toy example:
D -> E -> B -> A -> C
A -> C -> D -> E -> B
A -> C -> E -> D -> B
E -> A -> D -> C -> B
Note that in all scenarios, both D and E are ready, in no particular order, before B is ready. That’s the only commonality.
If you stop to ponder for a moment, you’ll recognize that a really easy way you could not only get into trouble but also cripple yourself from trivially porting your application to take advantage of the XDomain loader would be if you constructed logic that depended on the relative order of modules loading. For example, if you were counting on module A in the working example to perform a synchronous dojo.xhrGet request to retrieve some kind of global object that gets used throughout your application and module B, C, D, or E relied on it, you’d have a race condition on your hands if you tried to go with an XDomain build because there’s no guarantee that A will finish loading before any of the other modules; maybe it will, but maybe it won’t. For XDomain loading scenarios, if you truly wanted to guarantee that A would load before every other module, you’d have to go as far as to identify A as a dependency such that everything else that needs to block on it would block on it.
Getting Back to How debugAtAllCosts Works
And as Paul Harvey would say, “now you know…the rest of the story.”