5

Question: Why did wrapping my JavaScript library in an anonymous function fix my race condition?

Note: I am also interested in commentary on good solutions for synchronously and asynchronously loading external JavaScript resources.

I am working on a project that involves writing a browser extension using crossrider. If you are not familiar with cross rider extensions they are implemented in JavaScript, you have a background page that can communicate with an app page. The app page can runs with each open tab and can manipulate the DOM. I plan to load most of the extension code remotely when the user requests it to open. Recently I ran in to a race condition when I added a script tag to the page referencing Raphael, and then something I built out of graffle

From what I understand if a page is loaded with those script tags already in place, then the execution will take place synchronously, although since I was appending the script tags the execution took place asynchronously. This is the opposite problem that most people seem to have. Messing around with it I learned that wrapping my code graffle code in an anonymous function fixed my race condition. Why? I read this post on wrapping whole Javascript files in anonymous functions, and that doesn't seem to having anything to do with race conditions.

My calling code:

var scriptsToLoad   = [ "http://example/Scripts/lib/raphael.js",
                "http://example/Scripts/lib/graffle.js",
                "http://example/Scripts/lib/log4javascript.js"];

for(var i in scriptsToLoad) {
    (function(){
        $("head")
            .append($("<script></script>")
                .attr("type", "text/javascript")
                .attr("src", scriptsToLoad[i]));
    })()
}
Community
  • 1
  • 1
TMB
  • 4,683
  • 4
  • 25
  • 44
  • Do you have any libraries at your disposal? – Yahel Oct 05 '11 at 03:13
  • jquery comes prepackaged with the extensions. I want to swap it out for MooTools, but that's besides the point. – TMB Oct 05 '11 at 03:15
  • 1
    Probably not related to your problem: If you iterate through an Array using `for..in`, you'll also hit the `length` property. Check to see if you're appending a ` – Ates Goral Oct 05 '11 at 03:16
  • thats right, I am supposed to guard the inside of my loop with an if clause, what property should i detect? – TMB Oct 05 '11 at 03:19
  • 1
    You should be using `for`, not `for..in` (but you could get away with a `if(scriptsToLoad.hasOwnProperty(i)){}` wrapper. – Yahel Oct 05 '11 at 03:23
  • 2
    @AtesGoral That's not true; the `length` property isn't enumerable. Your advice is still sound, just for two other reasons: performance, and the possibility that somebody has added other junk to the Array prototype. – sethobrien Oct 05 '11 at 03:32
  • 7
    Just because a race condition no longer has a visible symptom doesn't mean it's been "fixed". There could be various reasons why what you tried "solved" the problem but unless you can find some justification from the ecma script standard or other documentation then I'm not sure how you can be certain the race condition is really gone. Do you see the same results across a range of browsers? – davmac Oct 05 '11 at 03:32
  • 1
    Seconding davmac's comment. The behaviour of race conditions **by definition** depends on the exact interleaving of events at runtime, not just on what your code says. It is very common for race condition bugs to sometimes happen, sometimes not. My honours supervisor used to say that it is impossible to debug race conditions out of your program, you have to **know** that your design **ensures** that there are none. – Ben Oct 05 '11 at 04:14
  • @davmac I agree, I can't really try this across a range of browsers since I am already using features of the framework that are chrome only. I do like Dylon Edwards post below, it doesn't answer the question, but it does bypass it. – TMB Oct 05 '11 at 04:14
  • @sethobrien Yikes. You're right, `length` won't make an appearance. I got it confused with custom property behaviour. – Ates Goral Oct 05 '11 at 05:56

1 Answers1

2

Regarding your question, I do not believe that there has been any particular standard set for the order in which <script /> tags are loaded and evaluated; it is easy to introduce race conditions.

Regarding (a)synchronous script loading, with jQuery, do this:

var scriptsToLoad = [
    "http://example/Scripts/lib/raphael.js",
    "http://example/Scripts/lib/graffle.js",
    "http://example/Scripts/lib/log4javascript.js"
];

$.each(scriptsToLoad, function (index, script) {
    $.ajax({
        url      : script,
        async    : false,
        dataType : 'script'
    });
});
Dylon
  • 1,730
  • 15
  • 14
  • The key is to set the `async` flag to `false` to make the request for each script synchronous. – Dylon Oct 05 '11 at 03:52
  • jQuery will automatically, globally evaluate the scripts as they are loaded when the `dataType` flag is `script`. If you can load the scripts asynchronously, just use `$.getScript(script);` – Dylon Oct 05 '11 at 03:56
  • 4
    Just a comment, but script tags that are *not* dynamically added to the DOM will actually block, and load in-order. – Peter Oct 05 '11 at 04:47
  • 1
    I don't really know what appending the `` tags to the document within closures is supposed to accomplish. It appears to me as though somebody noticed a positive correlation between doing so and the scripts loading in the correct order, but failed to determine the causation and made some incorrect assumptions. – Dylon Oct 05 '11 at 08:03