Search code examples
javascriptjquerygetscript

Having difficulty working with global variables and $.getScript


I have the following script which does not work properly:

function getWidgetContent( widget ) {
    if(widget.script!=null){
        $global_widget_id = widget.widget_id;
        $.getScript( "js/" + widget.script, function() {
            $( ".widget_header_title_" + widget.widget_id ).append( widget.title );
        });
    }
}

This is called as follows:

for ( j = 0; j <= widget_data.d.length - 1; j++ ) {
    getWidgetContent( widget_data.d[j] );
}

I thought this would run the function, within the function, assign a value to the global value, then run the $.getScript per iteration of the loop. But that is not happening. It seems to iterate through the loop until the loop is finished, lets say it loops 3 times, assign a value to the global value each time, i.e. 3 times, and then it finally goes and does the $.getScript.

Ofcourse that will not working as it will now only use the last assignment of the global value in the $.getScript file 3 times...

How do I change this so it assigns a value to the global variable, runs the $.getScript. When done, continue with the original loop, assign the next value to the global variable, run the $.getScript until the loop has finished.


Solution

  • You're getting this because $.getScript is an asynchronous method. In this case, it means that the method returns immediately before the script has finished loading, and continues executing code after it.

    What this means is that something like:

    $.getScript('a.js', function () {
        console.log('Loaded a');
        }); 
    $.getScript('b.js', function () {
        console.log('Loaded b');
        }); 
    $.getScript('c.js', function () {
        console.log('Loaded c');
        }); 
    // Output could be:
    // Loaded c
    // Loaded a
    // Loaded b
    

    This means that all of the script files requests can be done simultaneously but it also means that the order is not deterministic (fixed).

    Use Promises

    You can ensure that the getWidgetContent is called sequentially by chaining promises if you are using jQuery 1.5 and above. However, the pitfall of this method is that you will not be concurrently loading all the script requests at the same time, the requests will be sent one after another after the previous one has completed.

    Make sure you return the result of $.getScript which is a Deferred Object (I made minimal changes here, just note the return statements):

    function getWidgetContent( widget ) {
      if(widget.script!=null){
        $global_widget_id = widget.widget_id;
        return $.getScript( "js/" + widget.script, function() {
            $( ".widget_header_title_" + widget.widget_id ).append( widget.title );
            });
      }
      return null;
    }
    

    New method to perform a new getWidgetContent after a previous promise is fulfilled (completion of previous action):

    function doGetWidgetContentAfter(promise, widget) {
      return promise.then(function () {
          return getWidgetContent( widget );
          });
    }
    

    Calling it:

    var promise = $.when(true);
    for ( j = 0; j <= widget_data.d.length - 1; j++ ) {
      promise = doGetWidgetContentAfter( promise, widget_data.d[j] );
    }
    

    Explanation

    What doGetWidgetContentAfter does is that when the said promise is complete then call the getWidgetContent function for widget. The then method returns a promise that completes when any internal methods promise completes.

    I know it's starting to sound complicated, but do play around and experiment with it. :)