[javascript] How to include multiple js files using jQuery $.getScript() method

I am trying to dynamically include javascript files into my js file. I did some research about it and find jQuery $.getScript() method would be a desired way to go.

// jQuery
$.getScript('/path/to/imported/script.js', function()
{
    // script is now loaded and executed.
    // put your dependent JS here.
    // what if the JS code is dependent on multiple JS files? 
});

But I am wondering whether this method can load multiple scripts at one time? Why I am asking this is because sometimes my javascript file is depending on more than one js files.

Thank you in advance.

This question is related to javascript jquery html promise

The answer is


What you are looking for is an AMD compliant loader (like require.js).

http://requirejs.org/

http://requirejs.org/docs/whyamd.html

There are many good open source ones if you look it up. Basically this allows you to define a module of code, and if it is dependent on other modules of code, it will wait until those modules have finished downloading before proceeding to run. This way you can load 10 modules asynchronously and there should be no problems even if one depends on a few of the others to run.


You can give this a try using recursion. This will download them in sync, one after another until it completes downloading the whole list.

var queue = ['url/links/go/here'];

ProcessScripts(function() { // All done do what ever you want

}, 0);

function ProcessScripts(cb, index) {
    getScript(queue[index], function() {
        index++;
        if (index === queue.length) { // Reached the end
            cb();
        } else {
            return ProcessScripts(cb, index);
        }
    });
}

function getScript(script, callback) {
    $.getScript(script, function() {
        callback();
    });
}

Shorter version of Andrew Marc Newton's comprehensive answer above. This one does not check the status code for success, which you should do to avoid undefined UI behaviour.

This one was for an annoying system where I could guarantee jQuery but no other includes, so I wanted a technique short enough to not be farmed off into an external script if forced into it. (You could make it even shorter by passing the index 0 to the first "recursive" call but force of style habits made me add the sugar).

I'm also assigning the dependency list to a module name, so this block can be included anywhere you need "module1" and the scripts and dependent initialization will only be included/run once (you can log index in the callback and see a single ordered set of AJAX requests runnning)

if(typeof(__loaders) == 'undefined') __loaders = {};

if(typeof(__loaders.module1) == 'undefined')
{
    __loaders.module1 = false;

    var dependencies = [];

    dependencies.push('/scripts/loadmefirst.js');
    dependencies.push('/scripts/loadmenext.js');
    dependencies.push('/scripts/loadmelast.js');

    var getScriptChain  = function(chain, index)        
    {
        if(typeof(index) == 'undefined')
            index = 0;

        $.getScript(chain[index], 
            function()
            {
                if(index == chain.length - 1)
                {
                    __loaders.module1 = true;

                    /* !!!
                        Do your initialization of dependent stuff here 
                    !!! */
                }
                else 
                    getScriptChain(chain, index + 1);
            }
        );
    };

    getScriptChain(dependencies);       
}

This function will make sure that a file is loaded after the dependency file is loaded completely. You just need to provide the files in a sequence keeping in mind the dependencies on other files.

function loadFiles(files, fn) {
    if (!files.length) {
        files = [];
    }
    var head = document.head || document.getElementsByTagName('head')[0];

    function loadFile(index) {
        if (files.length > index) {
            var fileref = document.createElement('script');
            fileref.setAttribute("type", "text/javascript");
            fileref.setAttribute("src", files[index]);
            head.appendChild(fileref);
            index = index + 1;
            // Used to call a callback function
            fileref.onload = function () {
                loadFile(index);
            }
        } else if(fn){
            fn();
        }
    }
    loadFile(0);
}

I implemented a simple function to load multiple scripts in parallel:

Function

function getScripts(scripts, callback) {
    var progress = 0;
    scripts.forEach(function(script) { 
        $.getScript(script, function () {
            if (++progress == scripts.length) callback();
        }); 
    });
}

Usage

getScripts(["script1.js", "script2.js"], function () {
    // do something...
});

Loads n scripts one by one (useful if for example 2nd file needs the 1st one):

(function self(a,cb,i){
    i = i || 0; 
    cb = cb || function(){};    
    if(i==a.length)return cb();
    $.getScript(a[i++],self.bind(0,a,cb,i));                    
})(['list','of','script','urls'],function(){console.log('done')});

based on answer from @adeneo above: Combining both loading of css and js files

any suggestions for improvements ??

// Usage
//$.getMultiResources(['script-1.js','style-1.css'], 'assets/somePath/')
//  .done(function () {})
//  .fail(function (error) {})
//  .always(function () {});

(function ($) {
  $.getMultiResources = function (arr, pathOptional, cache) {
    cache = (typeof cache === 'undefined') ? true : cache;
    var _arr = $.map(arr, function (src) {
      var srcpath = (pathOptional || '') + src;
      if (/.css$/i.test(srcpath)) {
        return $.ajax({
          type: 'GET',
          url: srcpath,
          dataType: 'text',
          cache: cache,
          success: function () {
            $('<link>', {
              rel: 'stylesheet',
              type: 'text/css',
              'href': srcpath
            }).appendTo('head');
          }
        });

      } else {
        return $.ajax({
          type: 'GET',
          url: srcpath,
          dataType: 'script',
          cache: cache
        });
      }
    });
    //
    _arr.push($.Deferred(function (deferred) {
      $(deferred.resolve);
    }));
    //
    return $.when.apply($, _arr);
  };
})(jQuery);

You could make use of the $.when-method by trying the following function:

function loadScripts(scripts) {
  scripts.forEach(function (item, i) {
    item = $.getScript(item);
  });
  return $.when.apply($, scripts);
}

This function would be used like this:

loadScripts(['path/to/script-a.js', 'path/to/script-b.js']).done(function (respA, respB) {
    // both scripts are loaded; do something funny
});

That's the way to use Promises and have a minimum of overhead.


Use yepnope.js or Modernizr (which includes yepnope.js as Modernizr.load).

UPDATE

Just to follow up, here's a good equivalent of what you currently have using yepnope, showing dependencies on multiple scripts:

yepnope({
  load: ['script1.js', 'script2.js', 'script3.js'],
  complete: function () {
      // all the scripts have loaded, do whatever you want here
  }
});

This works for me:

function getScripts(scripts) {
    var prArr = [];
    scripts.forEach(function(script) { 
        (function(script){
            prArr .push(new Promise(function(resolve){
                $.getScript(script, function () {
                    resolve();
                });
            }));
        })(script);
    });
    return Promise.all(prArr, function(){
        return true;
    });
}

And use it:

var jsarr = ['script1.js','script2.js'];
getScripts(jsarr).then(function(){
...
});

Great answer, adeneo.

It took me a little while to figure out how to make your answer more generic (so that I could load an array of code-defined scripts). Callback gets called when all scripts have loaded and executed. Here is my solution:

    function loadMultipleScripts(scripts, callback){
        var array = [];

        scripts.forEach(function(script){
            array.push($.getScript( script ))
        });

        array.push($.Deferred(function( deferred ){
                    $( deferred.resolve );
                }));

        $.when.apply($, array).done(function(){
                if (callback){
                    callback();
                }
            });
    }

Sometimes it is necessary to load scripts in a specific order. For example jQuery must be loaded before jQuery UI. Most examples on this page load scripts in parallel (asynchronously) which means order of execution is not guaranteed. Without ordering, script y that depends on x could break if both are successfully loaded but in wrong order.

I propose a hybrid approach which allows sequential loading of dependent scripts + optional parallel loading + deferred objects:

_x000D_
_x000D_
/*_x000D_
 * loads scripts one-by-one using recursion_x000D_
 * returns jQuery.Deferred_x000D_
 */_x000D_
function loadScripts(scripts) {_x000D_
  var deferred = jQuery.Deferred();_x000D_
_x000D_
  function loadScript(i) {_x000D_
    if (i < scripts.length) {_x000D_
      jQuery.ajax({_x000D_
        url: scripts[i],_x000D_
        dataType: "script",_x000D_
        cache: true,_x000D_
        success: function() {_x000D_
          loadScript(i + 1);_x000D_
        }_x000D_
      });_x000D_
    } else {_x000D_
      deferred.resolve();_x000D_
    }_x000D_
  }_x000D_
  loadScript(0);_x000D_
_x000D_
  return deferred;_x000D_
}_x000D_
_x000D_
/*_x000D_
 * example using serial and parallel download together_x000D_
 */_x000D_
_x000D_
// queue #1 - jquery ui and jquery ui i18n files_x000D_
var d1 = loadScripts([_x000D_
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js",_x000D_
  "https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/i18n/jquery-ui-i18n.min.js"_x000D_
]).done(function() {_x000D_
  jQuery("#datepicker1").datepicker(jQuery.datepicker.regional.fr);_x000D_
});_x000D_
_x000D_
// queue #2 - jquery cycle2 plugin and tile effect plugin_x000D_
var d2 = loadScripts([_x000D_
  "https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/jquery.cycle2.min.js",_x000D_
  "https://cdn.rawgit.com/malsup/cycle2/2.1.6/build/plugin/jquery.cycle2.tile.min.js"_x000D_
_x000D_
]).done(function() {_x000D_
  jQuery("#slideshow1").cycle({_x000D_
    fx: "tileBlind",_x000D_
    log: false_x000D_
  });_x000D_
});_x000D_
_x000D_
// trigger a callback when all queues are complete_x000D_
jQuery.when(d1, d2).done(function() {_x000D_
  console.log("All scripts loaded");_x000D_
});
_x000D_
@import url("https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/blitzer/jquery-ui.min.css");_x000D_
_x000D_
#slideshow1 {_x000D_
  position: relative;_x000D_
  z-index: 1;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>_x000D_
_x000D_
<p><input id="datepicker1"></p>_x000D_
_x000D_
<div id="slideshow1">_x000D_
  <img src="https://dummyimage.com/300x100/FC0/000">_x000D_
  <img src="https://dummyimage.com/300x100/0CF/000">_x000D_
  <img src="https://dummyimage.com/300x100/CF0/000">_x000D_
</div>
_x000D_
_x000D_
_x000D_

The scripts in both queues will download in parallel, however, the scripts in each queue will download in sequence, ensuring ordered execution. Waterfall chart:

waterfall chart of scripts


There's a plugin out there that extends jQuery's getScript method. Allows for asynchronous and synchronous loading and uses jQuery's caching mechanism. Full disclosure, I wrote this. Please feel free to contribute if you find a better method.

https://github.com/hudsonfoo/jquery-getscripts


I ran into a number of issues with multi script loading inculding one issue with (at least in Chrome) same domain hot loading of scripts not actually running after being successfully loaded by Ajax where as Cross Domain works perfectly fine! :(

The selected answer to original question does not work reliably.

After many many iterations here is my final answer to getScript(s) and loading asynchronously multiple scripts in a specific strict order with per script loaded callback option and overall callback on completion, Tested in jQuery 2.1+ and modern versions of Chrome, Firefox plus the forsaken Internet Explorer.

My test case was loading files for a THREE.JS webGL render then starting the render script when THREE global became available using an interval check passed to an anonymous function call to onComplete.

The Prototype function ( getScripts )

function getScripts( scripts, onScript, onComplete )
{
    this.async = true;
    this.cache = false;
    this.data = null;
    this.complete = function () { $.scriptHandler.loaded(); };
    this.scripts = scripts;
    this.onScript = onScript;
    this.onComplete = onComplete;
    this.total = scripts.length;
    this.progress = 0;
};

getScripts.prototype.fetch = function() {
    $.scriptHandler = this;
    var src = this.scripts[ this.progress ];
    console.log('%cFetching %s','color:#ffbc2e;', src);

    $.ajax({
        crossDomain:true,
        async:this.async,
        cache:this.cache,
        type:'GET',
        url: src,
        data:this.data,
        statusCode: {
            200: this.complete
        },
        dataType:'script'
    });
};

getScripts.prototype.loaded = function () {
    this.progress++;
    if( this.progress >= this.total ) {
        if(this.onComplete) this.onComplete();
    } else {
        this.fetch();
    };
    if(this.onScript) this.onScript();
};

How to use

var scripts = new getScripts(
    ['script1.js','script2.js','script.js'],
    function() {
        /* Optional - Executed each time a script has loaded (Use for Progress updates?) */
    },
    function () {
        /* Optional - Executed when the entire list of scripts has been loaded */
    }
);
scripts.fetch();

The function is as it is for I found using Deferred ( Deprecated now? ), When, Success & Complete in my trials to NOT be 100% reliable!?, Hence this function and use of statusCode for example.

You may want to add in error/fail handling behaviour if you wish.


Append scripts with async=false

Here's a different, but super simple approach. To load multiple scripts you can simply append them to body.

  • Loads them asynchronously, because that's how browsers optimize the page loading
  • Executes scripts in order, because that's how browsers parse the HTML tags
  • No need for callback, because scripts are executed in order. Simply add another script, and it will be executed after the other scripts

More info here: https://www.html5rocks.com/en/tutorials/speed/script-loading/

var scriptsToLoad = [
   "script1.js", 
   "script2.js",
   "script3.js",
]; 
    
scriptsToLoad.forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.body.appendChild(script);
});

Here is answer using Maciej Sawicki's one and implementing Promise as callback:

function loadScripts(urls, path) {
    return new Promise(function(resolve) {
        urls.forEach(function(src, i) {

            let script = document.createElement('script');        
            script.type = 'text/javascript';
            script.src = (path || "") + src;
            script.async = false;

            // If last script, bind the callback event to resolve
            if(i == urls.length-1) {                    
                // Multiple binding for browser compatibility
                script.onreadystatechange = resolve;
                script.onload = resolve;
            }

            // Fire the loading
            document.body.appendChild(script);
        });
    });
}

Use:

let JSDependencies = ["jquery.js",
                      "LibraryNeedingJquery.js",
                      "ParametersNeedingLibrary.js"];

loadScripts(JSDependencies,'JavaScript/').then(taskNeedingParameters);

All Javascript files are downloaded as soon as possible and executed in the given order. Then taskNeedingParameters is called.


Load the following up needed script in the callback of the previous one like:

$.getScript('scripta.js', function()
{
   $.getScript('scriptb.js', function()
   {
       // run script that depends on scripta.js and scriptb.js
   });
});

I have improved @adeneo script so it will load all scripts in the specified order. It doesn't do chain loading, so it's very fast, but if you want even faster, change the 50 ms wait time.

$.getMultiScripts = function(arr, path) {

    function executeInOrder(scr, code, resolve) {
        // if its the first script that should be executed
        if (scr == arr[0]) {
            arr.shift();
            eval(code);
            resolve();
            console.log('executed', scr);
        } else {
            // waiting
            setTimeout(function(){
                executeInOrder(scr, code, resolve);
            }, 50);
        }
    }

    var _arr = $.map(arr, function(scr) {

        return new Promise((resolve) => {
            jQuery.ajax({
                type: "GET",
                url: (path || '') + scr,
                dataType: "text",
                success: function(code) {
                    console.log('loaded  ', scr);
                    executeInOrder(scr, code, resolve);
                },
                cache: true
            });
        });

    });
        
    _arr.push($.Deferred(function( deferred ){
        $( deferred.resolve );
    }));
        
    return $.when.apply($, _arr);
}

Usage is the same:

var script_arr = [
    'myscript1.js', 
    'myscript2.js', 
    'myscript3.js'
];

$.getMultiScripts(script_arr, '/mypath/').done(function() {
    // all scripts loaded
});

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to promise

Axios handling errors typescript: error TS2693: 'Promise' only refers to a type, but is being used as a value here Syntax for async arrow function Angular 2: How to call a function after get a response from subscribe http.post How to use fetch in typescript Returning Promises from Vuex actions Use async await with Array.map Getting a UnhandledPromiseRejectionWarning when testing using mocha/chai using setTimeout on promise chain Why is my asynchronous function returning Promise { <pending> } instead of a value?