Olly Hodgson
Olly Hodgson

Reputation: 15785

Is the callback on jQuery's getScript() unreliable or am I doing something wrong?

I'm using the following bit of script to load another one:

$.getScript("CAGScript.js", function () {
    try {
        CAGinit();
    } catch(err) {
        console.log(err);
    }
});

The idea is that $.getScript loads the script, then executes the callback when it's done. CAGInit() is a function that lives in CAGScript.js.

The problem is that roughly half the time, CAGInit() doesn't fire (in any browser). Logging to the Firebug console reports that it's not defined. The rest of the time it works perfectly.

Does anyone have any ideas what I'm doing wrong?

Thanks.

Upvotes: 21

Views: 25952

Answers (11)

Asad Ali Khan
Asad Ali Khan

Reputation: 307

My way is

setTimeout(function(){ 
  $.getScript( "CAGScript.js");
  }, 
2000);

this same issue we face in angular also when we want to update data in $scope and it is solved by using $timeout in same manner in angular...

Upvotes: 0

Miguel Madero
Miguel Madero

Reputation: 1948

Seems like jQuery handles cross-domain and same domain requests just fine now. For cross domain requests, it will add a script tag and subscribe to error and load events and appropriately call your done and error callbacks, it's an error only if a request fails, errors evaluating the script will still be considered a success as far as the request is concerned. For same-origin, it will do an XHR request and then do a globalEval and once it's done, call your done callback, if anything fails in the process, it will call your error callback.

Using XHR has a few issues, it breaks the debugger, sourcemaps and it relies on a globalEval, which has issues with ContentSecurityPolicy. For that an consistency, we simply add a script tag when we load scripts.

var scriptElement = $("<script>").prop({src: url, async: true}); scriptElement.one('load', ()=> ...); scriptElement.one('error', (evt)=> ...); document.head.appendChild(scriptElement[0]);

If you want to do it without jQuery search for Dynamically importing scripts in this article

Hope this helps.

Upvotes: 0

jgabios
jgabios

Reputation: 114

In order to make sure CAGScript.js is LOADED and EXECUTED before calling CAGinit function, the surest way is to have the function call inside CAGScript.js.

CAGScript.js:



    ...
    /*
    your code
    */
    function CAGinit(){
    ...
    }
    ...
    /* last line */
    CAGinit();

and then, in your main file just call getScript():

 

    $.getScript("CAGScript.js");

Upvotes: 4

lockedside
lockedside

Reputation: 1

I wanted to have something similar to auto_load in PHP implemented in JS, the typical solution is using try-catch on all functions.. when the function is missing we jump to downloading its library.. My solution is taken from Pieter solution as follows:

function CheckUserPW(username,password){
    try{
       loginCheckUserPW(username,password);
    } catch(err) {
        $.getScript('login.js', function(xhr){ 
            eval(xhr);
            loginCheckUserPW(username,password);
        });
    }
}

Upvotes: 0

gibbitz
gibbitz

Reputation: 211

Just wanted to offer some insight I have on this issue. The callback won't fire if there is an error in the code you are loading and (at least on Chrome with jQuery 1.7.1) the error will be swallowed. I discovered that I had a stray curly brace from autocomplete in the code I was loading and the callback function didn't fire. Intermittent behavior here could be caused by changing the loaded code between tests.

Upvotes: 21

natevw
natevw

Reputation: 17912

I've gotten bit by this issue on Chrome as well. jQuery occasionally calls the success callback as if all is well, but the script hasn't actually been loaded properly. In our case, the solution was to simply change from trusting jQuery:

<script>
    $.getScript("helpers.js", function () {
        // use helpers....or: EXPLODE (wheeeee!)
        helpers.doSomething();
    });
</script>

To trusting the browser:

<script src="helpers.js"></script>
<script>
    helpers.doSomething();
</script>

I'm finding again and again that "less jQuery == less edge cases == less bugs". And usually more readable code, too!

Upvotes: 3

Fabrice
Fabrice

Reputation: 3100

I noticed the same issue with FF 3.6.

A solution is to load the script synchronously.

As mentioned in jQuery's documentation, getScript is shorthand for:

$.ajax({
  url: url,
  dataType: 'script',
  success: success
});

Everything works fine if I use the following instead of getScript:

$.ajax({
  url: url,
  dataType: 'script',
  success: success,
  async: false
});

Upvotes: 23

capnhairdo
capnhairdo

Reputation: 41

Yeah, I've discovered too that getScript is unreliable in FireFox, firing the callback before the script has been downloaded and/or executed. (Using JQuery 1.41 & FireFox 3.6. Issue doesn't seem to afflict IE, Chrome or Opera.)

I haven't done extensive testing, but it seems to only happen with some specific scripts...not sure why.

RaYell's suggestion doesn't work, as getScript will report success even though the script has not yet been eval'ed. Pieter's suggestion in most cases causes the code to be eval'ed twice, which is inefficient and can cause errors.

This alternative seems to work where getScript does not. Note that it seems to be adding a SCRIPT DOM element, whereas getScript does XHR.

http://www.diveintojavascript.com/projects/sidjs-load-javascript-and-stylesheets-on-demand

Upvotes: 1

Pieter
Pieter

Reputation: 91

Just had the same problem on firefox, solved it with a little hack.

Applying it to your example:

$.getScript("CAGScript.js", function (xhr) {
    try {
        CAGinit();
    } catch(err) {
        eval(xhr);
        CAGinit();
    }
});

Basically forcing an evaluation of the XHR response if it fails to do so by itself.

Upvotes: 9

James
James

Reputation: 111950

If the file is held on the same domain then jQuery will use XHR to retrieve its contents and then will globally "eval" it. This should work fine but if you're having problems then I'd suggest using the alternative method of injecting a script tag. Unfortunately, jQuery doesn't expose this functionality so you'll have to do it yourself:

var script = jQuery('<script/>').attr('src', 'CAGSCript.js').appendTo('head');

var timer = setInterval( function(){ 
    if (window.CAGInit !== undefined) {
        clearInterval(timer);
        script.remove();
        // Do your stuff:
        CAGInit();
    }
}, 200);

It'd be best to abstract this to a function; the above is just an example...

Upvotes: 11

RaYell
RaYell

Reputation: 70434

I think you can start by checking testStatus of the callback function to make sure that the script was really loaded. Callback function has two parameters, more defails on those you can find on jQuery Docs

$.getScript("CAGScript.js", function (data, textStatus) {
    if (textStatus === "success") {
        try {
            CAGinit();
        } catch(err) {
            console.log(err);
        }
    } else {
        console.log("script not loaded");
    }
});

Upvotes: 0

Related Questions