For example:
ajaxCall() {
$.ajax({
...
...
success: function(response) {
event2();
}
});
}
If we have a call like:
event1();
ajaxCall();
event3();
Would it be always guaranteed that the order of execution of events be
event1() then event2() and event3() without setting the async flag ?
AJAX is Asynchronous JAX :) There's your answer.
If you want to make a synchronous call, you need to set the async: false flag. But then the success callback won't be called at all and you would need to put the event2(); line just below the $.ajax call.
Also see Mike C's answer. The synchronous calls are deprecated.
In general, you're right if you set async to false. However, synchronous AJAX has been deprecated and jQuery officially dropped support for it after v1.8.
So I would suggest you avoid trying to use synchronous AJAX requests.
No.
The callback function will fire when the event that triggers it (i.e. the HTTP response) happens.
A function you call immediately after you assign the callback will still be called immediately.
The order of execution will still be:
event1();
ajaxCall();
$.ajax();
event3();
success();
event2();
Related
I have a synchronous function (that is an existing working code which means the author doesn't want me to change the code) and now on the task I am working on, the senior developer wants me to wait for this synchronous function to return a callback or wait for it to finish first before executing the next code. Is there a way to wrap an existing synchronous function to an asynchronous function?
StateChangeService.doChangeState('form', {id: '123123112313'})
This is inside the code itself
// They don't want to change this to an async function
doChangeState: function (state, options) {
changeToState(state, options);
}
They don't want me to change anything on the StateChangeService so is there a way to get wrap this around an async method instead?
Sure, use a promise for that.
asyncChangeState(state, options) {
return Promise.resolve(doChangeState(state, options));
}
// To call it
asyncChangeState(x, y).then(result => {...});
The callback in the then will only run once your function has been completed.
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.
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.
I've been trying to get my head around the jQuery Deferred object. My intention is to inspect each ajax response (success/fail). I want to do this without interfering with other code that declares a typical $.ajax().done().fail() request.
I've user the $.ajaxPrefilter() to get each ajax request before it is executed. Using the .then() method on the jqXHR object, I've managed to add a function that will be called before the .done() method placed on the original $.ajax() call
The code below will print out the following:
def done
def then
2nd ajax prefilter then
2nd ajax done
2nd ajax then
ajax done
ajax then
What I don't understand is why the prefilter step executes first. I would have expected it to have been executed last, or not at all.
The behaviour is what I want, but I don't understand why.
// this is a typical usage of deferred with two done functions added, the second via .then()
var def = $.Deferred();
def.done(function(){
document.write("def done<br>");
});
def.then(function(){
document.write("def then<br>");
});
def.resolve();
// this is a typical ajax request with a done function added, followed by another using .then()
$.ajax("/echo/json/").done(function(){
document.write("ajax done<br>");
}).then(function(){
document.write("ajax then<br>");
});
// for the third request i intercept and call the .then() method
$.ajaxPrefilter(
function( options, originalOptions, jqXHR ) {
jqXHR.then(function(data, textStatus, jqXHR){
document.write("2nd ajax prefilter then<br>");
});
});
// create a typical ajax request. these will be executed after the prefilter .then()
$.ajax("/echo/json/").done(function(){
document.write("2nd ajax done<br>");
}).then(function(){
document.write("2nd ajax then<br>");
});
Thanks in advance for any help
UPDATE: ------------
From #Bergi response, the code below demonstrates how the $.ajaxPrefilter() is called before the done().
$.ajaxPrefilter(
function( options, originalOptions, jqXHR ) {
document.write("prefilter function within $.ajax call<br>");
jqXHR.then(function(data, textStatus, jqXHR){
document.write("2nd ajax prefilter then<br>");
});
});
var functionToRunWhenDoneIsCalled = function() {
document.write("done is called function<br>");
return function(){
document.write("2nd ajax done<br>");
}
}
$.ajax("/echo/json/").done(
(functionToRunWhenDoneIsCalled)()
).then(function(){
document.write("2nd ajax then<br>");
});
This outputs:
prefilter function within $.ajax call
done is called function
2nd ajax prefilter then
2nd ajax done
2nd ajax then
Which answers my question about how the .then() method is attached to the deferred jqXHR object before the .done() method.
In your case, there is no difference between adding callbacks with .done() or with .then(). Using only .done() would be enough.
What I don't understand is why the prefilter step executes first. I would have expected it to have been executed last, or not at all.
Callbacks are executed in the order they are added to the deferred object. And the prefilter is executed inside of $.ajax, i.e. the callback is attached even before the $.ajax call returns and your done and then handlers can be attached.
All .then does if you don't return a deferred object is add another done fail and/or progress handler to the deferred object. with that in mind, it makes complete sense for the .then added in the pre-filter to execute before the one added after $.ajax() because the code in the pre-filter callback happened first. The callbacks get triggered first in first out.
What I don't understand is why the prefilter step executes first. I would have expected it to have been executed last, or not at all.
You've attached another "thing to do" to the jqXHR that is associated with the ajax request. Since it's a pre-filter, that gets attached before the standard done/fail that the ajax request uses. Handlers run in the order they were attached and the prefilter one is therefore first.
Note that since the prefilter only attached a single function in the .then() method, nothing will run if the request fails for some reason. Sounds like you'd want to have the second (failure handler) arg as well.
As for the completion order of the two different ajax requests, that is not predictable. It will just depend on which one returns first.
I was wondering if it was possible to have a function call a function that has an ajax request, and continue executing when the ajax request finished. Here is example pseudo code.
function func1(){
//do things
func2();
//**How would I get this code to execute after the ajax request is finished.
}
function func2(){
$.ajax({
url: "test.html",
context: document.body,
success: function(){
//do some things
}
});
I do not want to execute everything in a callback function, as this function is called many times in a loop.
Thanks!
You have two choices.
Use a callback function for the processing that happens after the AJAX call.
Use a synchronous "JAX" call ... remember, the A in AJAX is Asynchronous.
Otherwise, there's not a good way to accomplish what you describe.