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

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);
};

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;

Javascript code isn't getting into my document ready listener. (forge iOS)

This is my entire javascript file for the home page of my app. Any ideas as to why it never gets into the document ready listener?
var photos;
forge.request.ajax({
url: "http://photos-url.com/pics.json",
dataType: "json",
success: function(data) {
photos = data;
},
error: function(error) {
forge.logging.info("Couldn't fetch pics!");
}
});
//logging output works here
$(function() {
//logging output doesn't work here
//I'm trying to append to the html here, but it never gets into this code
});
Cross-domain requests are prohibited for security reasons (same as in desktop browsers). You must configure environment to allow requests to your domain. Look at https://trigger.io/docs/current/api/modules/request.html for details.
json files are usually allowed to be read from cross domain and even if this one would't be, I still doubt it could affect ready event. I'm not using document ready function on my page as I was having simillar issues (it fires few minutes after page is loaded, or doesn't fire at all). You could try window.onload or document.onload events. I'd also try to find out how document.readyState behaves and eventually check it manually with interval or try to bind event listener to it.

Dynamic Script Tags for JSON requests... detecting if there is a XXX error?

I do a bunch of json requests with dynamic script tags. Is it possible to detect if there's an error in the request (eg. 503 error, 404 error) and run something on detection of the error?
use ajax instead. AFAIK there is no way to detect if a script tag loads or not, and if not, why it didn't load. Using ajax you can load the json and it will tell you why it didn't load.
Using a library like jQuery this becomes very simple:
$.ajax({
type: "GET",
url: "test.js",
dataType: "script",
error: function(xhr, error, exception){
alert(xhr.status); //Will alert 404 if the script does not exist
}
});
AFAIK, there's no way to access status code of some external asset loaded from the document (such as script, style or image). Even detecting error (via, say, onerror event handler) is not that widely supported across browsers.
If whatever you're loading falls under SOP, use XHR which gives you access to response headers. Otherwise, you can try looking into recently introduced X-domain XHR.
I'm assuming you want this to work cross-domain, which is why you can't use XHR?
Try creating two script tags for each request, the first does your standard JSONP request, the second is basically an error handler.
If the first script tag executes, then clear the error handler in your callback. But if the first gets a 404, the error handler inside the second script tag will be run.
You probably also want to set a timeout, to cope with a slow JSONP response.
http://www.phpied.com/javascript-include-ready-onload/ ?
If you're using jQuery, check out jQuery-JSONP which is a jQuery plugin that does a fairly decent job of doing the <script> insertion for you as well as detecting fetch errors.
Quoting from the project page, jQuery-JSONP features:
error recovery in case of network failure or ill-formed JSON responses,
precise control over callback naming and how it is transmitted in the URL,
multiple requests with the same callback name running concurrently,
two caching mechanisms (browser-based and page based),
the possibility to manually abort the request just like any other AJAX request,
a timeout mechanism.
If you need to cross domains (and need the page to work portably), you have to use dynamic script tags.
If you have access to the remote server, you can pass back an error code from the server, and have the server page return 200.
Whether you have access or not, you can use setTimeout when you create the script tag, passing a function that will trigger an error if it expires before the jsonp handler is called. Make sure that the jsonp handler aborts if the error handler has been called.
You'll need to track each request through a global collection, but you'll gain the ability to cancel and count requests. This is similar to the way that XHR objects are managed by a library like jQuery.
If you want to detect errors, listen for an error event and compare the fileName property of the error with the file name of the script. If they match, you then handle the error. The thing is, I think that the fileName property is Firefox and Opera-only. Most browsers that have a stacktrace for errors can also simulate this behaviour.
Here's an example, as requested by Eric Bréchemier:
var getErrorScriptNode = (function () {
var getErrorSource = function (error) {
var loc, replacer = function (stack, matchedLoc) {
loc = matchedLoc;
};
if ("fileName" in error) {
loc = error.fileName;
} else if ("stacktrace" in error) { // Opera
error.stacktrace.replace(/Line \d+ of .+ script (.*)/gm, replacer);
} else if ("stack" in error) { // WebKit
error.stack.replace(/at (.*)/gm, replacer);
loc = loc.replace(/:\d+:\d+$/, "");
}
return loc;
},
anchor = document.createElement("a");
return function (error) {
anchor.href = getErrorSource(error);
var src = anchor.href,
scripts = document.getElementsByTagName("script");
anchor.removeAttribute("href");
for (var i = 0, l = scripts.length; i < l; i++) {
anchor.href = scripts.item(i).src;
if (anchor.href === src) {
anchor.removeAttribute("href");
return scripts.item(i);
}
}
};
}());

Handling errors in jQuery.getScript

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");
})();

Categories

Resources