15
votes

The getScript docs say about the success callback that:

"The callback is fired once the script has been loaded but not necessarily executed."

But in my testing that doesn't seem to be true. For a host page with:

var startTime = new Date();

$.getScript("test.js")
 .done(function( script, textStatus ) {
    console.log( textStatus );
    console.log( "Done callback executing now.")
  })
  .fail(function( jqxhr, settings, exception ) {
    console.log("error." );
});

loading the following "test.js" script which ties up the UI for 5 seconds:

console.log("ajaxed script starting to execute.");
var newTime = new Date();
while (newTime - startTime < 5000) {
    newTime = new Date();
}
console.log("elapsed time", newTime - startTime);
console.log("ajaxed script finished executing.");

results in the same predictable console output in both FF & Chrome:

ajaxed script starting to execute.
elapsed time 5000 
ajaxed script finished executing.
success
Done callback executing now. 

In other words, the success callback does not ever fire until the loaded script is both loaded and executed. This seems to be because in the jQuery source, the globalEval function is calling the script immediately:

converters: {
    "text script": function( text ) {
        jQuery.globalEval( text );
        return text;
    }
}

So are the docs wrong? If they are correct, then in what specific cases will the success callback fire before the script is executed?

3
Can your take your question and markdown over to github.com/jquery/api.jquery.com/issues ? :) - gnarf
The documentation is definitely wrong, thanks for the heads up :) - Julian Aubourg
Thanks, github issue filed: github.com/jquery/api.jquery.com/issues/420 - Ben
For what it is worth: One of my applications suffers from exactly this race condition. The external script, which does nothing but setting a global variable, almost always executes before the callback. But once in a while, it does not, and the global variable remains undefined when the callback runs, crashing the application. I wonder if there is a reliable way to delay the execution of the callback until the script has been executed... - Zero3

3 Answers

0
votes

From what I can tell looking at the source, I agree with your conclusion that the promise should always resolve after the script has executed.

However my memory tells me that there was some weirdness about injecting <script> tags in one of the many browsers jQuery 1.x support(s|ed). The problem browser was firing loaded callbacks before the script had executed at some point, or we wouldn't of added a note about it in the docs.

I'm pretty sure the implementation of $.getScript has changed since then and the problem line in the docs is no longer relevant, either way we need to get the question in front of the jQuery core team. You really should open an issue on github - feel free to cc @gnarf.

0
votes

This is a quote from a response to the GitHub issue filed by the original poster:

$.getScript("https://platform.twitter.com/widgets.js").done(..) with some twitter media will fire the done code without waiting for the execution to happen.

Thus, the documentation seems to be correct, though perhaps not as descriptive as one might wish.

0
votes

I have ran into this issue in 1.7.1 version of jQuery. Before version 2.1.0 the forementioned $.globalEval() function was using indirect call of infamous eval() to run code in global environment.

So it certainly was an issue before.

In version 2.1.0 they have switched to script insertion because of certain lacks in indirect eval call. You can find more info here if you like.

http://perfectionkills.com/global-eval-what-are-the-options/

In newer implementation with script insertion this line is doing the magic

context.head.appendChild( script ).parentNode.removeChild( script );

Unless there is some strange underlying browser mechanism it seems script must be executed immediately, before being removed from DOM tree.