Using getScript synchronously

32,337

Solution 1

As I said, it's relatively easy to chain Ajax calls with promise objects. Now, it don't see why the scripts have to be loaded one after the other, but you will have a reason for it.

First though I would get rid of the switch statement if you are only calling the same function with different arguments. E.g. you can put all the script URLs in a map:

var scripts = {
    '#FFF': '...',
    '#000': '...'
    // etc.
};

You can chain promises by simply returning another promise from a callback passed to .then [docs]. All you need to do is start with a promise or deferred object:

var deferred = new $.Deferred();
var promise = deferred.promise();

for (var i in divlist) {
    // we need an immediately invoked function expression to capture
    // the current value of the iteration 
    (function($element) {
        // chaining the promises, 
        // by assigning the new promise to the variable
        // and returning a promise from the callback
        promise = promise.then(function() {
            return loadScript(
                scripts[$element.css("background-color")], 
                $element
            );
        });
    }($('#' + divlist[i])));
}

promise.done(function() {
    // optional: Do something after all scripts have been loaded
});

// Resolve the deferred object and trigger the callbacks
deferred.resolve();

In loadScript, you simply return the promise returned from $.getScript or the one returned by .done:

function loadScript(script_url, $element){
    // Unrelated stuff here!!!

    return $.getScript(script_url).done(function(){
        //  Unrelated stuff here
        // do something with $element after the script loaded.
    });
}

The scripts will all be called in the order the are access in the loop. Note that if divlist is an array, you really should use normal for loop instead of a for...in loop.

Solution 2

This worked for me, and may help you.

$.ajax({
    async: false,
    url: "jui/js/jquery-ui-1.8.20.min.js",
    dataType: "script"
});

Basically, I just bypassed the shorthand notation and added in the async: false

Solution 3

Do you know that $.getScript accepts a callback function that is called synchronously after the script is loaded?

Example:

$.getScript(url,function(){
//do after loading script
});

I have 2 more solutions: a pure js one and one for multiple js load.

Solution 4

Try this way, create array with deferred objects and used $.when with "apply"

var scripts = [
    'src/script1.js',
    'src/script2.js'
];

var queue = scripts.map(function(script) {
    return $.getScript(script);
});

$.when.apply(null, queue).done(function() {
    // Wait until done, then finish function
});
Share:
32,337

Related videos on Youtube

VashGH
Author by

VashGH

Updated on July 25, 2022

Comments

  • VashGH
    VashGH almost 2 years

    I'm writing an engine that requires the use of getScript quite extensively. I've pushed it into its own function, for ease of use, but now I need to make sure that the function itself is synchronous. Unfortunately, I can't seem to make getScript wait until the script it loads is actually finished loading before proceeding. I've even tried setting jQuery's ajax asynch property to false before making the call. I'm thinking of using jQuery's when/done protocol, but I can't seem to wrap my head around the logic of placing it inside a function and making the function itself synchronous. Any help would be very much appreciated!

    function loadScript(script){
    //Unrelated stuff here!!!
    $.when(
    $.getScript(script,function(){
        //Unrelated stuff here!!!
    })).done(function(){
        //Wait until done, then finish function
    });
    }
    

    Loop code (by request):

    for (var i in divlist){
            switch($("#"+divlist[i]).css({"background-color"})){
                case #FFF:
                loadScript(scriptlist[0],divlist[i]);
                break;
            case #000:
                loadScript(scriptlist[2],divlist[i]);
                break;
            case #333:
                loadScript(scriptlist[3],divlist[i]);
                break;
            case #777:
                loadScript(scriptlist[4],divlist[i]);
                break;
        }
    }
    
    • Felix Kling
      Felix Kling about 11 years
      There is no need to make loadScript synchronous. Just return the promise object form $.getScript and let the calling code bind a callback. Or why exactly do you think it has to be synchronous?
    • VashGH
      VashGH about 11 years
      I'm looping through a variable length array (varies by the page it's on) of divs using a for-in loop and performing a series of instructions with them, including loading scripts. The problem is, the scripts have to be loaded after the previous has finished loading and executing. Unfortunately, I've not been able to find a suitable way of "waiting" for an object to exist in javascript, yet. Intervals work well, except for the fact that it is all inside of that for-in loop for the div's.
    • Felix Kling
      Felix Kling about 11 years
      Are you applying the same commands on each div (I assume so since it's a loop) but load a different script for each div? Could you post some code? It's easy to chain Ajax calls using deferred objects.
    • VashGH
      VashGH about 11 years
      Updated with the basic idea. Perhaps you can think of up a better way of doing it.
    • Felix Kling
      Felix Kling about 11 years
      I provided a suggestion.
  • VashGH
    VashGH about 11 years
    That runs synchronously and makes sure the script is finished loading prior to executing any other code?
  • Roger C
    Roger C about 11 years
    Right, scripts are loaded synchronously unless you add the async attribute
  • Roger C
    Roger C about 11 years
    I could have sworn they were loaded synchronously but it seems it's not the case, I'm sorry for misleading you.
  • VashGH
    VashGH about 11 years
    My main problem is that I need it within a function (because I use the same 20 lines or so of code all over the place), and I need the function itself to be synchronous. That code just gives me a callback for when the scripts finish loading. getScript already has that feature.
  • kupriyanenko
    kupriyanenko about 11 years
    Maybe then you should look at AMD architecture and use requirejs with modules dependencies? requirejs.org/docs/api.html#modulename
  • VashGH
    VashGH about 11 years
    deferred.resolve() throws an error (function doesn't exist) and the code doesn't execute as expected.
  • Felix Kling
    Felix Kling about 11 years
    Mmmh. The deferred / promise objects changed a bit in the recent jQuery versions. I updated the example and hope it works now.
  • VashGH
    VashGH about 11 years
    That did the trick. Thanks for not only solving my problem, but exposing me to a new method of performing chained tasks. This deferred/promise logic is very intriguing!
  • Felix Kling
    Felix Kling about 11 years
    Yeah, it's great way to decouple code, abstract from synchronous/asynchronous code, etc. I love it :) You can read more about the original proposal for JavaScript (which was more basic) here: wiki.commonjs.org/wiki/Promises/A and of course on Wikipedia: en.wikipedia.org/wiki/Futures_and_promises.
  • foxontherock
    foxontherock almost 5 years
    That's the only method I found to do a sync "fallback" of some js files, without using "document.write". All others methods using DOM manipulation were always async even when setting async=false. The dom "appendBefore" occurs too late. The "xhr" method is really sync and on the next line, my variables are available to use. I know it's deprecated, but in how many years... document.write is also deprecated, and still works. And I think there's no other "sync" solution of scripts loading fallback directly in "html source".
  • kmuenkel
    kmuenkel about 3 years
    Much appreciated for answering the question directly. I'm swimming in some legacy code at the moment, where handling things 'properly' isn't an option without massive refactors, so this is a good stop-gap for me.