Receiving warning about Synchronous XMLHttpRequest in asynchronous function - javascript

My browser (or the JQuery framework, to be more specific) prints the following warning to the browser's console:
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.
I understand the warning, but not why it appears. To my understanding the call of asyncFunct() is asynchronous and therefore shouldn't print the warning message.
I'm using this JavaScript code:
var i18nObject;
$(document).ready(function () {
// execute asyncFunct() asynchronously
setTimeout(asyncFunct, 3000);
});
function asyncFunct() {
alert(i18n("rejoin.title"));
}
function i18n(key) {
if (typeof i18nObject == 'undefined') {
// i18n not loaded yet, need to load it first
$.ajax({
url: 'UrlTo/i18n.json',
async: false,
dataType: 'json',
success: function (response) {
i18nObject = response;
}
});
}
if (i18nObject.hasOwnProperty(key)) {
// key found
return i18nObject[key];
} else {
// key not found
return key;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
To face some concerns (from the comments) about async: false: This was deliberately chosen, as I need the function to return a value. I understand that using synchronous calls is a bad idea if they lock the UI. But why does this apply to my problem here? I want the call to asyncFunct() to be asynchronous.
Why am I receiving this warning, and is it possible to disable it? (to prevent unnecessary text in the console)

Edit
Actually, since you are putting the sync call inside of a timeout, it appears that the call does happen asynchronously, as you correctly pointed out (see this jsbin for an example):
setTimeout(function() {
$.ajax({
method: 'GET',
url: 'http://api.openweathermap.org/data/2.5/weather?q=London,uk',
success: function(res) {
console.log(res);
},
async: false
});
}, 1000);
console.log('something synchronous');
If you open up the console, you'll notice that (as expected), something synchronous is logged first, and then a little over a second later, the result from our API is logged.
So I feel like there are two parts to this question that need to be addressed.
The first part is, why are you forcing a sync call to happen async, using a deprecated API, when you could just avoid the timeout altogether and simply make your XMLHttpRequest naturally asynchronous?
The second part is about suppressing the warning. It appears that the warning happens mistakenly, but the warning isn't looking at the fact that you made the sync request inside of a timeout. It's simply looking at the fact that you are using a deprecated API. In other words, the warning only cares about the usage of the deprecated API itself, and not the specific implementation.
However, if you really want to suppress these warnings, it'll be an all or nothing deal (see this answer for an example):
// define this as an empty function in your own code
console.log = function() {};
Original answer
To my understanding the call of asyncFunct() is asynchronous and therefore shouldn't print the warning message.
False.
Your call is not asynchronous. It is synchronous, because you have async: false.
This warning is because synchronous requests block the main thread (the same thread that handles UI, for example). This can ruin the user's experience using the site/app. Thus, the warning shows up in the console.

Related

Force Chrome to check if a $.ajax call has finished?

Good afternoon guys -
Is there a well known way to check if a $.ajax call has finished?
-- FOR INSTANCE --
Let's say I'm using a $.ajax call to load in a large number of leaflet polygons from a .geojson file (asynchronously as a layer group). Under normal circumstances, this happens almost immediately - but the user has elected to load a large job this time around. The user has assumed that the set of polygons has been loaded and attempts to do something with this group of layers prematurely - only to find that nothing happens (the layer group doesn't actually exist yet in the browser).
My intuition (I'm new to web development) is to have some sort of global flag that's set for the dependent algorithm to check. We would set the flag prior to loading the layers, and then change it to some other value in the .done({}) section of the $.ajax call.
-- UPDATE --
I've reworked the code to allow users to choose whether or not they wish to retry the request, and also force the browser to wait some time before retrying the request.
However, I've also found the the issue seems to be with Chrome. Firefox appears to be able to handle the $.ajax.always callback as soon as it finishes (in other words, the $.ajax.always callback will interrupt the regular flow of javascript).
Chrome appears to block the $.ajax.always callback and only lets it execute after all other javascript has finished running (no interrupt).
This particular question stemmed from a debug case that automated user input from within a loop - and since Chrome doesn't call the $.ajax.always callback until the current process is complete, the process isn't marked as completed.
Example Code:
procBox = []; // Global scope, stands for Process Box
function loadPolygons() {
procBox["loadPolygons"] = "running";
$.ajax({
// ajax things here
}).done(function() {
procBox["loadPolygons"] = "done";
}).fail(function() {
// failure stuff here
});
}
function dependentFunction() {
if procBox["loadPolygons"] === "done") {
// The meat of the function (dependentFunction things here)
} else {
// Backup case that allows the browser to retry the request
/* --
* If this fires, the server is still trying to process the
* ajax request. The user will be prompted to retry the request,
* and upon agreement, the function will be called again after
* 1 second passes.
*/
var msg = "Oops! It looks like we're still fetching your "
+ "polygons from the server. Press OK to retry.";
if (confirm(msg)) {
setTimeout(dependentFunction, 1000);
}
}
}
This approach seems to work well in Firefox - the alert() stops JavaScript execution and gives it a chance for the .done({}) callback to occur. But for some reason, the while loop never allows the .done({}) callback to complete in Chrome!
Does anyone know of a better approach for this than using flags or async: false?
I appreciate any answers and knowledge out there!
There are numerous ways to do :
as already sugegsted you can use
since you use jQuery, you can use custom events https://learn.jquery.com/events/introduction-to-custom-events/:
$(document).on("myajax:done", function(){ dependentFunction();})
$.ajax({...}).done(function(){
$(document).trigger("myajax:done")
});
or even global ajax events
https://api.jquery.com/category/ajax/global-ajax-event-handlers/
but really consider why not to do something like
procBox = {onLoadPolygons:dependentFunction}; // Global scope
function loadPolygons() {
$.ajax({
// ajax things here
}).done(function() {
procBox["onLoadPolygons"]();
}).fail(function() {
// failure stuff here
});
}
function dependentFunction() {
alert("Please wait for the polygons to load");
dependentFunctionThings();
}
function dependentFunctionThings(){
// Do dependent function things...
}
UPD:
if you ensist on your structure, and still want to use blocking function
use setInterval to perform check
function dependentFunction() {
var h = setInterval(function() {
if (procBox["loadPolygons"] == "done") {
clearInterval(h);
// Do dependent function things...
}
}, 100);
}
Or wrap it up into a Promise (http://caniuse.com/#feat=promises)
function dependentFunction() {
(new Promise(function(resolve) {
var h = setInterval(function(){
if (procBox["loadPolygons"] == "done") {
clearInterval(h);resolve();
}
}, 100);
})).then(function(){
// Do dependent function things...
});
}
But I still believe that something wrong in your structure
From the docs :
.ajaxComplete()
Whenever an Ajax request completes, jQuery triggers the ajaxComplete event. Any and all handlers that have been registered with the .ajaxComplete() method are executed at this time.
http://api.jquery.com/ajaxcomplete/

Understanding jQuery.Deferred in the context of jQuey.AJAX (again)

I concede that, despite hours of reading and attempting, I am fundamentally unable to grasp something about Deferred promises and asynchrony in general.
The goal on my end is real, real simple: send some data to the server, and react to the contents of the response conditionally.
The response will always be a JSON object with save and error keys:
{ "save": true, "error":false}
// or
{ "save" : false,
"error" : "The server has run off again; authorities have been notifed."}
I have tried dozens and dozens of variations from the jQuery API, from other stackexchange answers, from tutorials, etc.. The examples all seem concerned with local asynchronous activity. When I need is some ability to be made aware when the AJAX request has either finished and returned a response I can inspect and make decisions about, or else to know that it's failed. Below, I've used comments to explain what I think is happening so someone can show me where I'm failing.
I know this is a repost; I am, apprently, worse than on average at grasping this.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
/* when(), as I understand it, should fire an event to be
responded to by then() when it's contents have run their course */
var result = $.when(
/* here I believe I'm supposed to assert what must complete
before the when() event has fired and before any chained
functions are subsequently called */
/* this should return a jqXHR object to then(), which is,
I'd thought, a queue of functions to call, in order,
UPON COMPLETION of the asynchronous bit */
$.post("my/restful/url", postData))
.then( function() {
/* since "this" is the jqXHR object generated in the $.post()
call above, and since it's supposed to be completed by now,
it's data key should be populated by the server's response—right? */
return this.data;
});
// alas, it isn't
console.log(result.data);
// >> undefined
Most examples I can find discuss a timeout function; but this seems, as I understand, to be a failsafe put in place to arbitrarily decide when the asynchronous part is said to have failed, rather than a means of stalling for time so the request can complete. Indeed, if all we can do is just wait it out, how's that any different from a synchronous request?
I'll even take links to a new read-mes, tutorials, etc. if they cover the material in a different way, use something other than modified examples from the jQuery API, or otherwise help this drooling idiot through the asynchronous mirk; here's where I've been reading to date:
jQuery API: Deferred
JQuery Fundamentals
jQuery Deferreds promises asynchronous bliss (blog)
StackOverflow: timeout for function (jQuery)
Update
This is in response to #Kevin B below:
I tried this:
var moduleA = {
var moduleB = {
postData: {"id":7, "answer":"Ever since I went to Disneyland..."};
save: function() {
return $.post("path/to/service", postData, null, "JSON");
}
};
var result = this.moduleB.save();
result.done(function(resp) {
if (resp.saved == true) {
// never reached before completion
console.log("yahoo");
} else {
console.log("Error: " + resp.error);
// >> undefined
}
});
}
You are over-complicating your code. You cannot get the data to outside of the callback, no matter how many deferred/promises you create/use (your sample creates 3 different deferred objects!)
Use the done callback.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
$.post("my/restful/url", postData).done(function (result) {
console.log(result.save, result.error);
});
You seem to have a misunderstanding of both asynchronous requests, the Promise pattern, and Javascripts mechanism of passing functions as an argument.
To understand what's really happening in your code I suggest you use a debugger and set some breakpoints in the code. Or, alternatively, add some console.logs in your code. This way you can see the flow of the program and might understand it better. Also be sure to log the arguments of the function you pass as an argument in the then()-method, so you understand what is passed.
ok you got it half right. the problem is that when you execute the console.log the promised is not yet fulfilled the asynchronous nature of the promises allows the code to execute before that ajax operation is done. also result is a deferred not a value, you need to handle your promised with .done instead of .then if you wish to return a value otherwise you'll continue passing promises.
so that said
var result={};
$.when(
$.post("my/restful/url", postData))
.done( function(data) {
result.data=data;
});
// here result is an object and data is a undefined since the promised has no yet been resolve.
console.log(result.data);

How to stop execution of next Statements if an Async function returns any errors?

I have below couple of statements with in a javascript function.
postTransaction function is having a async call, which is calling a webservice and returns success or failure. I have two call back methods for success(transactionSuccess) and failure(transactionFailure).
Here my problem is if the service got failure i should stop executing the next statement i.e return requestDetails(); I dont want to use setTimeOut function here. Any other ways to handle my situation?
function doSomeThing () {
postTransaction(objSelectedAccount,transactionSuccess,transactionFailure);
return requestDetails();
}
function requestDetails () {
return true;
}
function postTransaction () {
$.ajax('URL', {
method: "POST",
dataType: "json",
data: {},
success: function (payload) {
callBackSucces(payload);
},
error: function(xhr, statusText, ex) {
callBackFailure(xhr);
}
});
}
You can't stop the next statement from executing based on the result of an async operation. That's the whole point of async operations! You also don't want to make the ajax request a blocking request, because that will freeze the UI until the request completes -- a bad user experience that might also lead to a "script has stopped responding" alert from the browser.
The best you can do is move whatever processing you want delayed into the success handler for the ajax call. Don't think of the ajax call as something that "returns success or failure"; it doesn't actually return anything (in the JavaScript sense). Instead, think of it as something that generates either a success or a failure event at some future time and code your app to handle the events.
If requestDetails() may not be called until the transactionSuccess was called, then you may put the requestDetails into the transactionSuccess. And your function doSomehting should not be a synchronous function.
So, change your code to something like this:
function doSomething(callback) {
postTransaction(objSelectedAccount, function () {
// success and do something..
callback requestDetails();
}, transactionFailure);
}
A function, which have a asynchronous step, should also be a asynchronous function.
I hope this could help you, and please forgive my poor English..
function doSomeThing () {
postTransaction(objSelectedAccount,transactionSuccess,transactionFailure);
}
function requestDetails () {
return true;
}
function postTransaction () {
$.ajax('URL', {
method: "POST",
dataType: "json",
data: {},
success: function (payload) {
requestDetails(payload);
},
error: function(xhr, statusText, ex) {
callBackFailure(xhr);
}
});
}
OR,
use synchronous request.
There is no way to change an asynchronous operation into a synchronous operation (i.e. to "wait" for the outcome and then call or not something else).
You must rework and break down your logic into smaller parts so that the operation are done (or just started) in the "success" callback.
Note that many asynchronous operations are indeed just requests to a server and it's possible to make a synchronous request (i.e. performing a "get" blocking while wainting for the answer instead of using callbacks) but this is considered not a good idea in general because the application becomes less responsive.
Unfortunately (and for reasons I don't fully understand) Javascript doesn't expose the event dispatching primitive and thus is impossible to use what is normally done in other event-driven environments like GUIs when asynchronous is very inconventient (e.g. requesting of a confirmation) that is a "nested event loop".
The fact that you cannot build a synchronous operation from an asynchronous one is also the reason for which in node for many functions there are the equivalent Synch versions that would be impossible for the application programmer to create.

Syntax for javascript object implementing an AJAX GET and custom event

I've got a couple of questions about this small snippett adapted from a tutorial I found here.
var loader = (function ($, host) {
return {
loadTemplate: function (path) {
var tmplLoader = $.get(path)
.success(function (result) {
$("body").append(result);
})
.error(function (result) {
alert("Error Loading Template");
}) // --> (1) SEMICOLON?
// (2) How does this wire up an event to the previous
// jQuery AJAX GET? Didn't it already happen?
tmplLoader.complete(function () {
$(host).trigger("TemplateLoaded", [path]);
});
}
};
})(jQuery, document);
Is there supposed to be a semicolon there?
It seems like the AJAX GET is happening and then an event is getting wired to it - what am I missing here?
Is there supposed to be a semicolon there?
It's optional, but recommended.
It seems like the AJAX GET is happening and then an event is getting wired to it - what am I missing here?
AJAX is asynchronous, so it's very unlikely the request will be already completed right after sending it. So, there's time to add another callback. And even if there weren't, it would work anyway, since jQuery implements those callbacks with promises. See example here.
With javascript, and ajax in particular it is important to understand how the browser goes about executing your code. When you make the request for remote data via an ajax GET, the rest of your code is still executing. Imagine if as soon as you made a request for some JSON to a busy server, lets say it takes a couple seconds, and everything on your page stops working during that time period. It would be very difficult to write code that wasn't difficult for the user to interact with. Luckily ajax is async, meaning it makes the request and an carries on as usual until the complete event (or equivalent) is fired. This is what executes your code pertinent to the data you just received. So when you specify that callback at the bottom of your snippit, you are telling the browser, "go do your thing for now but when you hear back from the server, do all of these things".
Oh yeah, and semicolons are optional, but as a best practice, most people use them.
They are assigning the $.get to a variable and then adding a complete handler to it.
It's the same as doing this:
$.get('/path'), function(){
//success callback
}).error(function(e){
//errors
}).complete(function(){
//always run
});
Just an unusual way of doing it.

JQuery / Javascript inline callback

In tornado we have gen module, that allows us to write constructions like this:
def my_async_function(callback):
return callback(1)
#gen.engine
def get(self):
result = yield gen.Task(my_async_function) #Calls async function and puts result into result variable
self.write(result) #Outputs result
Do we have same syntax sugar in jquery or other javascript libraries?
I want something like this:
function get_remote_variable(name) {
val = $.sweetget('/remote/url/', {}); //sweetget automatically gets callback which passes data to val
return val
}
You describe the function as "my_async_function", but the way you use it is synchronous rather than asynchronous.
Your sample code requires blocking -- if "my_async_function" were truly asynchronous (non-blocking), the following line of code self.write(result) would execute immediately after you called "my_async_function". If the function takes any time at all (and I mean any) to come back with a value, what would self.write(result) end up writing? That is, if self.write(result) is executed before result ever has a value, you don't get expected results. Thus, "my_async_function" must block, it must wait for the return value before going forward, thus it is not asynchronous.
On to your question specifically, $.sweetget('/remote/url/', {}): In order to accomplish that, you would have to be able to block until the ajax request (which is inherently asynchronous -- it puts the first A in AJAX) comes back with something.
You can hack a synchronous call by delaying the return of sweetget until the XHR state has changed, but you'd be using a while loop (or something similar) and risk blocking the browser UI thread or setting off one of those "this script is taking too long" warnings. Javascript does not offer threading control. You cannot specify that your current thread is waiting, so go ahead and do something else for a minute. You could contend with that, too, by manually testing for a timeout threshold.
By now one should be starting to wonder: why not just use a callback? No matter how you slice it, Javascript is single-threaded. No sleep, no thread.sleep. That means that any synchronous code will block the UI.
Here, I've mocked up what sweetget would, roughly, look like. As you can see, your browser thread will lock up as soon as execution enters that tight while loop. Indeed, on my computer the ajax request won't even fire until the browser displays the unresponsive script dialog.
// warning: this code WILL lock your browser!
var sweetget = function (url, time_out) {
var completed = false;
var result = null;
var run_time = false;
if (time_out)
run_time = new Date().getTime();
$.ajax({
url: url,
success: function(data) {
result = data;
completed = true;
},
error: function () {
completed = true;
}
}); // <---- that AJAX request was non-blocking
while (!completed) { // <------ but this while loop will block
if (time_out) {
if (time_out>= run_time)
break;
run_time = new Date().getTime();
}
}
return result;
};
var t = sweetget('/echo/json/');
console.log('here is t: ', t);
Try it: http://jsfiddle.net/AjRy6/
Versions of jQuery prior to 1.8 support sync ajax calls via the async: false setting. Its a hack with limitations (no cross-domain or jsonp, locks up the browser), and I would avoid it if possible.
There are several available libraries that provide some syntactic sugar for async operations in Javascript. For example:
https://github.com/caolan/async
https://github.com/coolaj86/futures
...however I don't think anything provides the synchronous syntax you are looking for - there is always a callback involved, because of the way JavaScript works in the browser.

Categories

Resources