views:

227

answers:

3

Hi All,

I am trying to write an ultra simple solution to load a bunch of JS files asynchronously. I have the following script below so far. However the callback is sometimes called when the scripts aren't actually loaded which causes a variable not found error. If I refresh the page sometimes it just works because I guess the files are coming straight from the cache and thus are there quicker than the callback is called, it's very strange?

var Loader = function () {

}
Loader.prototype = {
    require: function (scripts, callback) {
        this.loadCount      = 0;
        this.totalRequired  = scripts.length;
        this.callback       = callback;

        for (var i = 0; i < scripts.length; i++) {
            this.writeScript(scripts[i]);
        }
    },
    loaded: function (evt) {
        this.loadCount++;

        if (this.loadCount == this.totalRequired && typeof this.callback == 'function') this.callback.call();
    },
    writeScript: function (src) {
        var self = this;
        var s = document.createElement('script');
        s.type = "text/javascript";
        s.async = true;
        s.src = src;
        s.addEventListener('load', function (e) { self.loaded(e); }, false);
        var head = document.getElementsByTagName('head')[0];
        head.appendChild(s);
    }
}

Is there anyway to test that a JS file is completely loaded, without putting something in the actual JS file itself, because I would like to use the same pattern to load libraries out of my control (GMaps etc).

Invoking code, just before the tag.

var l = new Loader();
l.require([
    "ext2.js",
    "ext1.js"], 
    function() {
        var config = new MSW.Config();
        Refraction.Application().run(MSW.ViewMapper, config);
        console.log('All Scripts Loaded');
    });

Thanks for any help.

+1  A: 

There is nothing wrong with your code from what I can tell, this is just a bug in Chrome (it does it with window.onload also.)

I'd add to the function that is triggered in the "load" function. If the variable exists, execute the JS code, but if it doesn't, use a setTimeout to check again in 500ms or so.

Aaron Harun
You can also test for a function being present: e.g., if you want to load jQuery this way, you can test for the `$` function being present in global scope: `if ('$' in window) /* do something */;`
Marcel Korpel
Thanks added this check as well :)
Gcoop
A: 

Just in case you find this useful, I've created an async utility library that would let you write the above code as:

var Loader = function () {}

Loader.prototype = {
    require: function (scripts, callback) {
        async.map(scripts, this.writeScript, callback);
    },
    writeScript: function(src, callback) {
        var s = document.createElement('script');
        s.type = "text/javascript";
        s.src = src;
        s.addEventListener('load', function (e) { callback(null, e); }, false);
        var head = document.getElementsByTagName('head')[0];
        head.appendChild(s);
    }
}

If you're doing lots of asynchronous calls it has some quite powerful features :)

http://caolanmcmahon.com/async.html

Caolan
A: 

What about jQuery....

$.getScript('abc.js');

Above code will load the "abc.js" script file asynchronously....

Rajesh Shelar
I am using the xui framework already and don't want to include the jQuery framework as well, it will just add extra weight :)Although I didn't know jQuery could do this, thanks!
Gcoop