Handling errors in jQuery.getScript - javascript

jQuery's getScript function doesn't seem to support an error callback function. I can't use the global ajax error handling code here, a local error function would be ideal.
Documentation that the callback gets data/textStatus seems incorrect - the callback gets neither.
Any suggestions on how I detect that a call to getScript failed (server not available, for instance)?
EDIT: Just looked at source, and it seems like the callback is only invoked on success, with data always set to null and textStatus not defined (since it's a success-only callback, I presume). The documentation is very incorrect for this function.

As of jQuery 1.5 you can append a .fail to your call to getScript.
$.getScript('foo.js', function(){
//script loaded and parsed
}).fail(function(){
if(arguments[0].readyState==0){
//script failed to load
}else{
//script loaded but failed to parse
alert(arguments[2].toString());
}
})
http://api.jquery.com/jQuery.getScript/#handling-errors

For cross domain script tags, the success event fires but the error event does not; no matter what syntax you use. You can try this approach:
Create an error handler and set it to fire after few seconds using handle = window.setTimeout
Inside your success callback function, cancel the timeout using window.clearTimeout(handle)
Sample code:
var timeoutId; // timeout id is a global variable
timeoutId = window.setTimeout(function() {
alert("Error");
}, 5000);
$.getScript("http://other-domain.com/script.js", function(){
window.clearTimeout(timeoutId);
});

The global JQuery Ajax-ErrorHandler will work!
Prior to the $.getScript-Call setup the Error Handler to cach the error.
$(document).ajaxError(function(e, xhr, settings, exception) {
alert('error in: ' + settings.url + ' \n'+'error:\n' + exception );
});
As described in the JQuery manual: http://api.jquery.com/ajaxError/.

jquery.ajax has a alternative way to handle error
jQuery.ajax({
type: "GET",
url: 'http://www.example.com/script_test.js',
dataType: "script",
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log('error ', errorThrown);
},
success:function(){
console.log('success');
}
});

Unless you don't use jQuery 2.0+ $.getScript seem to be the wrong choice, because it does not provide any error handling capabilities while making cross-domain requests. None of those: fail, complete, error, statusCode will work. I've checked it with jQuery 1.11.2
Solution with setTimeout will be too slow if you have to load fallback script when the first will fail.
In this case script.onerror callback seem to be the cleanest way.
var script = document.createElement('script');
document.head.appendChild(script);
script.onload = function () {
// loaded
};
script.onerror = function () {
// failed
};
script.src = 'https://example.com/main.js';
In combination with $.Deferrred provides reliable way to build complex loaders.
var libLoaded = $.Deferred();
var script = document.createElement('script');
document.head.appendChild(script);
script.onload = libLoaded.resolve;
script.onerror = function () {
// load fallback script, no error handling
$.getScript('/fallbackLib.js')
.done(libLoaded.resolve)
};
script.src = 'https://example.com/lib.js';
$.when(libLoaded).then(
// fanally I can use my lib safly
);

This is a bit of a hack, but..
You could declare a variable inside the scripts you load and check for it after you've loaded a script (assuming that the complete-function still fires):
script_test.js:
var script_test = true;
And then:
$.getScript("script_test.js", function ()
{
if (typeof script_test !== undefined) alert("script has been loaded!");
});
Or you could just try and see if whatever is in your script, actually exists--functions, variables, objects, etc.
A more generic way to do this would be adding a self-executing function inside the scripts you want to load, and make them execute a function in your "main" script:
main_script.js:
function scriptLoaded(scriptName)
{
alert(scriptName + " loaded!");
}
$.getScript("script_test.js");
script_test.js:
(function ()
{
scriptLoaded("script_test.js");
})();

Related

HTML load order and dynamically added JS Files

I add some JS files dynamically to my HTML Header and I want to guarantee that my JS files finish loading before I continue to render my Body.
Events like load or DOMContentLoaded trigger after the loading is finished.
my body needs the added scripts to render properly but starts before the files are loaded fully.
Code Snippet:
...
<script>
$.ajax({
type: "Get",
url: '#Url.Action("GetLocalisationFiles", "Localisation")',
success: function (response) {
for (var file in response) {
var scriptName = response[file];
//Adding of the script(s)
let myScript = document.createElement("script");
myScript.setAttribute("src", scriptName);
document.head.appendChild(myScript);
//
}
}
});
window.addEventListener("load", LocalizationAdded);
function LocalizationAdded(e) {
alert("JS Files Finished Loading");
DevExpress.localization.loadMessages(RddsDataNavigator_LanguagePack_en);
}
</script>
</head>
<body class="dx-viewport">
<script>
alert("Body Started");
...
Is there any other event prior to the rendering of the body or an easy way to delay my body rendering?
I know I could manually add all Content that depends on the added scripts after the loading is finished but this seems fuzzy.
The dynamical adding of JS works as intended. My Problem is within the order it happens.
Thanks in advance for any help
Previous question:
How do I reference code in dynamically added js files?
We could question whether loading scripts following some user action, is such a good idea. You could instead load the relevant HTML content from the server (which could include script tags), if you really want to have a one-page experience, or else initiate a navigation, where again the server would get clues via HTTP request on what to generate next.
But, if we stick with this pattern, I would suggest using the onload property of script elements, and to use promises for awaiting all scripts to have been loaded.
Here is some code you could use. This demo loads two JS files after an AJAX call has come back with a response: one for the immutablejs library, the second for the momentjs library.
A new Promise will resolve when the script's load event fires, and Promise.all will resolve when this has happened for all scripts.
For the purpose of demo I replaced the URL with a mock, and also tampered with the response in order to produce a response as you have it:
// dummy server, just for demo
let url = "https://jsonplaceholder.typicode.com/todos/1";
console.log("Launching initial HTTP request...");
$.get(url).then(function (response) {
console.log("Received response. Loading scripts...");
// Overwriting the response with mock data:
response = {
immutable: "https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.min.js",
moment: "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.0/moment.min.js"
};
return Promise.all(Object.values(response).map(function (scriptName) {
return new Promise(function (resolve, reject) {
//Adding of the script(s)
let myScript = document.createElement("script");
myScript.setAttribute("src", scriptName);
myScript.onload = resolve;
myScript.onerror = reject;
document.head.appendChild(myScript);
//
});
}));
}).then(function () {
// All code that depends on the loaded scripts should be here, or called from here
console.log("All scripts loaded:");
console.log("Immutable: ", typeof Immutable);
console.log("moment: ", typeof moment);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Remark: the async: false option is deprecated. It is not good practice either. Instead use the Promise that jQuery returns for $.ajax, $.get, ...etc, and chain a then call where you continue processing the result (instead of a success handler). And on once you arrive in the world of promises, you'll find that using async and await syntax can simplify code.

async onload not working in this case

I have some code to asynchronously load an Ad on my site which looks like this:
<div id="ad-div"></div>
<script>function onAdReady() {
debug_log('Method onAdReady called');
new Ad(document.getElementById("ad-div"))
}
</script>
<script src="http://content.xxxxx.com/ads/ads.min.js" async onload="onAdReady()"></script>
The problem is that the onAdReady function is never beeing called. The reason for this might be that the html code which contains this snippet is beeing loaded via javascript in the first place like this:
// Initiate out_window_view
$.ajax({
url: loadPagePath("main.html"),
success: function (result) {
debug_log("Going in main.html view");
$("#content").html(result);
},
error: function (result) {
debug_log("Error, failed to load out_window_main view");
}
});
This code is beeing executed in the $(document).ready(function () {] in case that might matter.
Can anyone expalain to me why this is not working and provide me with a workaround or alternative way for solving this issue?
I don't understand why you need to add async to an ajax-generated-content. The purpose of async script is to allow the browser to keep on parsing the document without waiting for the script to fully load. The onload event is called immediately after the script has finished loading and before DOMReady. In other words, async's onload will not fire after DOMReady.
When you add the snippet to your page, the page has already finished parsing, so the async onload event won't fire.
The solution IMO is to remove the async part and just call the function after the <script> synchronously.
You want to load an ad when the page is loaded?
What do you see in the network tab from the debug tools (firebug or F12 in chrome/Firefox)?
Where do you call $( document ).ready() ?
https://learn.jquery.com/using-jquery-core/document-ready/
Can you tell me when you want to send the HTTP request?
If you are able to add the script programmatically, it will be async by default (reference here), and you can listen to the onload or onreadystatechange event (as you can read here, IE11 doesn't support onreadystatechange). Here is an example:
var setup = function(){
console.log('the script has been parsed');
};
var script = document.createElement("script");
script.src = "http://content.xxxxx.com/ads/ads.min.js";
document.getElementsByTagName("head")[0].appendChild(script);
// standard browser
script.onreadystatechange = function (){
if (this.readyState == 'complete'){
setup();
}
}
// IE
script.onload = setup;

Handling errors with jQuery $.getScript

I am using a function that automatically loads .js files for modules with jQuery .getScript() function. The problem is that if the loaded script has any errors in it I cannot find a way to display the error line from the file loaded. I found a way to display the error type and error message but not the error line.
jQuery.getScript(main.constants.BASEURL + main.modules[activeModule].path + 'js/functions.js')
.done(function() {
Module.init();
})
.fail(function(jqxhr, settings, exception) {
errorObj = {};
if (arguments[0].readyState == 0) {
errorObj.message = 'Failed to load script!';
//script failed to load
}
else {
errorObj.type = arguments[1];
errorObj.message = arguments[2]['message'];
errorObj.lineNo = ??; //This is what I need to find
//script loaded but failed to parse
}
console.log(errorObj);
});
If you dynamiclly add a new script tag to head you will see line numbers, use scripts element onload to init the module

Catching TCP reject errors with $.ajax() and jQuery 1.4.3

I am writing Ajax code for a site that is stuck to jQuery 1.4.3. It happens that my Ajax call receives a TCP reject (that is ok and part of the project). Unfortunately with this jQuery version complete is never called. With jQuery 2 it works and status contains error. I can't update the jQuery version.
Can the code be fixed that Ajax fails due to TCP rejects will be catched?
This is a simplified part of my code that reproduces the problem. It assumes that localhost:9999 is rejected.
$(document).ready(function() {
$.ajax({
url: 'http://localhost:9999',
crossDomain: true,
dataType: 'script',
complete: function(XMLHTTPRequest, status) {
console.log(status);
},
});
});
I set up a http://jsfiddle.net/c2Yt6/ where you can test it: even with jQuery 1.6.4 is doesn't work but jQuery 2.0.2 is ok.
How to catch TCP reject errors?
If you look at the code: http://code.jquery.com/jquery-1.4.3.js
script.onload = script.onreadystatechange = function() {
This part should execute in your code (add unminified and put a breakpoint there)
The problem is that the event listener function never gets executed. If I copy that code and add a line:
script.onerror=function(e){
console.log("some error",e);
};
Then the error is detected and logged.
Looks like a bug in jQuery 1.4.3. If all you want to do is load scripts then I suggest not using jQuery but code like this:
function loadScript(params){
var s = document.createElement("script");
s.src = params.url;
s.onload = function(){
s.parentElement.removeChild(s);
params.success();
};
s.onerror = function(){
s.parentElement.removeChild(s);
params.error();
};
document.body.appendChild(s);
};

how can I know this javascript coding flaw from browser as nothing shows up?

html is :
<input type='button' value='AddAAAA' id='tagbutton' onclick='add_tag_my();' />
javascript snippet:
function get_the_script(){
alert("function get_the_script");
$.getScript("js/global.js",function(){
alert("script loaded");
//addtag();
alert("after getscript");
});
}
function add_tag_my(){
alert("i am add_tag_my()");
get_the_script();
}
global.js contains:
golabl_var=12;
alert("new_selection =" + new_selection);
No declaration exists for the js variable new_selection , so when I run the script, i get output up to the code line : alert("function get_the_script"); No code output hence forward( 2 more alerts don't show up). But if i change the mentioned line in global.js into alert("new_selection"); I get the output of the 2 alert boxes as well.
My question is when I use the code mentioned first for global.js , I don't see any message in chrome console tab, so how can I know that that is the place where my prob resides?
If you use some older versions of jQuery you will get this error in console.
But in new versions the execution of dynamically loaded Javascript ( using eval function ) is inside a try..catch block ( line 7371 ). jQuery catches the exception so no error will be shown in console. But you can add error handlers for jQuery ajax function and receive the exception inside the function.
Either you can add a global ajax error handler like this
$("div.error").ajaxError(function (e, jqxhr, settings, exception) {
if (settings.dataType == 'script') {
$(this).html(exception);
}
});
function get_the_script() {
alert("function get_the_script");
$.getScript("js/global.js", function () {
alert("script loaded");
//addtag();
alert("after getscript");
});
}
Or you can use ajax function with error handlers instead of getScript
$.ajax({
url: "js/global.js",
dataType: "script",
success: function (data) {
alert("script loaded");
//addtag();
alert("after getscript");
},
error: function (xhr, textStatus, errorThrown) {
alert(errorThrown);
}
});
If you don't want to add any error handlers, but just want to identify the errors while developing , you can use the options " Don't pause on Exceptions ", " Pause on all Exceptions ", " Pause on uncaught Exceptions " by toggling the pause button at the bottom of Chrome web inspector in Scripts mode.

Categories

Resources