Anas Nakawa
Anas Nakawa

Reputation: 2015

Loading javascript files automatically with sequential callbacks

I have the following javascript list, each item in this list has a script file url and a callback function, what i want to do is to iterate through this list, load each script, then execute the callback function, and NOT moving to next item unless the current callback is done executing..


var data = { 
    scripts: [
        {
            file    : 'some-script.js',
            callback: function() {
                // do some code here
            },
        },
        {
            file    : 'another-script.js',
            callback: function() {
                // do some code here
            },
        },
        ...
    ]
};

how can I do this automatically, if Possible ?

Upvotes: 0

Views: 1218

Answers (4)

Q_Mlilo
Q_Mlilo

Reputation: 1787

(I'm assuming that you are using JSONP to load your external scripts) The problem that you will face is that these scripts will not load all at the same time.

Below is some code that will call your callback function as soon as the associated script is loaded.

    function include_js(url, callback) {  // http://www.nczonline.net
        var script = document.createElement("script");
        script.type = "text/javascript";

        if (script.readyState) {  //IE
            script.onreadystatechange = function(){
                if (script.readyState == "loaded" || script.readyState == "complete"){
                    script.onreadystatechange = null;
                    callback();
                }
            };
        } else {  //Others
            script.onload = function(){
                callback();
            };
        }
        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);
    }

//Edit

    for (var i = 0; i < data.scripts.length; i++) {
         include_js(data.scripts[i].file, data.scripts[i].callback);
    }

Upvotes: 2

T.J. Crowder
T.J. Crowder

Reputation: 1074258

It's possible if there's a symbol you can test for in each script, or if each script can execute a function call when it's done loading.

Loading the script is easy, as I'm sure you know:

var script = document.createElement('script');
script.src = /* ... the source path ... */;
document.body.appendChild(script);

(You'll see people appending to head instead; doesn't matter, and body is easy to find.)

But the hard part is knowing when it has been downloaded and executed. The only real ways of doing that are by polling to see if a new global symbol defined by the script has been defined, or by having the script actively call you back when it's done loading (a'la JSONP).

Either way, once you've detected the symbol or gotten the callback, you then move on to loading the next script.

Here's a quick-and-dirty sketch of doing it on the basis of finding a global symbol (a property on the window object):

// The scripts to load
var scriptList = { 
    scripts: [
        {
            file    : 'some-script.js',
            callback: function() {
                // do some code here
            },
            symbol  : "someSymbol",
            timeout : 30000 // We know this one is slow, give it 30 seconds
        },
        {
            file    : 'another-script.js',
            callback: function() {
                // do some code here
            },
            symbol  : "anotherSymbol"
        },
        // ...
    ]
};

// Triggering the load
loadScripts(scriptList);

// Library routines to do the load
function loadScripts(list)
{
    var index, timeout;

    // Start with the first one (loadNextScript starts with an increment)
    index = -1;
    loadNextScript();

    // This function loads the next script in the list; if there are no
    // more, it simply returns
    function loadNextScript()
    {
        var script;

        // Are there more?
        ++index;
        if (index < list.length)
        {
            // Yes, append a `script` element
            script = document.createElement('script');
            script.src = list.file;
            document.body.appendChild(script);

            // Determine when to time out
            timeout = new Date() + (list[index].timeout || 20000); // Time out in Xms, default 20 seconds

            // Start polling
            setTimeout(pollForScript, 0); // Async, but almost immediately (4-10ms on most browsers)
        }
    }

    // This function polls to see if the current script has loaded yet by
    // checking for a global symbol it defines.
    function pollForScript()
    {
        var result;

        // Has it been too long?
        if (new Date() > timeout)
        {
            // Yes
            result = "timeout";
        }
        else
        {
            // Has the symbol been defined?
            if (typeof window[list[index].symbol] !== "undefined")
            {
                // Yes
                result = "loaded";
            }
            else
            {
                // Nope, keep waiting
                setTimeout(pollForScript, 250); // Check every quarter-second
            }
        }

        // Did we get a result?
        if (result)
        {
            // Yes, do the callback telling it of the result
            try {
                list[index].callback(result);
            }
            catch (e) {
            }

            // Load the next script
            loadNextScript();
        }
    }
}

Upvotes: 1

Simon H
Simon H

Reputation: 1745

this is just something quick I wrote up and HAVENT TESTED, but it should do what you are after in a recursive fashion. Hope this helps:

var data = { 
    scripts: [
        {
            file    : 'some-script.js',
            callback: function() {
                // do some code here
            },
        },
        {
            file    : 'another-script.js',
            callback: function() {
                // do some code here
            },
        },
        ...
    ]
};


$(document).ready(function() {

    if(data.scripts.length > 0) {
        scriptLoader(0);
    }

});

function scriptLoader(i) {


    var currScript = data.scripts[i];


    $.getScript(currScript.file, function() {

      // execute your callback
      currScript.callback();

      // load next script
      if(i < data.scripts.length) {
        scriptLoader(i++);
      }

    });

}

Oh and btw this uses the JQuery javascript framework, just FYI.

Upvotes: 0

joni
joni

Reputation: 5462

Let the callback call your loadTheNextScriptInList or whatever function. You can do this automatically by adding an anonymous function to the onLoad event. This anonymous function should first call the callback set in the list, and then call your loadTheNextScriptInList function.

Upvotes: 0

Related Questions