Reputation: 360
I saw this everywhere on the web but i couldn't manage to fix my code to avoid this issue, simply I have an ajax function that I trigger when some buttons are clicked, sometimes I want an indicator (loading animation) to show, sometimes not, so i build my function:
function doAjax(action, todo, items, error_num, hide_indicator) {
items.action = action;
items.do = todo;
var postedObj;
$.ajax({
type: 'post',
dataType: 'html',
url: ajaxURL,
data: items,
async: false,
beforeSend: function() {
if (!hide_indicator) showIndicator();
},
success: function(data) {
if (data) {
ajaxObj = JSON.parse(data);
if (ajaxObj.ok) {
postedObj = ajaxObj;
} else {
alert(ajaxObj.error);
postedObj = false;
}
} else {
alert('[' + error_num + '] Something went wrong.');
postedObj = false;
}
if (!hide_indicator) hideIndicator();
},
error: function() {
alert('[' + error_num + '] Something went wrong.');
postedObj = false;
if (!hide_indicator) hideIndicator();
}
});
return postedObj;
}
And here is what I do on my buttons to call the function:
$(document).on('click', '.ad-single', function() {
var addataObj = doAjax('post_requests', 'get-ad-info', {"id": $(this).data('ad')}, '158', false); // false means DON'T hide the indicator
if (addataObj) {
loadContent(addataObj.ad);
}
});
OK, for now, everything works as expected on Firefox, when I click my button, the indicator shows up and wait until data is returned by ajax, then hide the indicator again.
This doesn't work on Chrome and Safari, the function works fine and return the data as expected but it seems that the hideIndicator()
function is called immediately, I couldn't know how to fix this.
Firefox:
Chrome and Safari:
Upvotes: 2
Views: 10206
Reputation: 23859
Using async: false
is evil. The Internet is full of such posts, and many browsers including Chrome and Safari will give you numerous warnings on making synchronous AJAX requests.
Fortunately, jQuery's promises can be used to overcome your problem. It doesn't change how your function works, and leverages the power of promises too to make async requests.
You can modify your existing function as follows, so that it returns an AJAX promise, instead of the result.
function doAjax(action, todo, items, error_num, hide_indicator) {
items.action = action;
items.do = todo;
return $.ajax({
type: 'post',
dataType: 'html',
url: ajaxURL,
data: items,
beforeSend: function() {
if (!hide_indicator) showIndicator();
}
})
.then(function(data) {
if (!hide_indicator) {
hideIndicator();
}
if (data) {
ajaxObj = JSON.parse(data);
if (ajaxObj.ok) {
return ajaxObj;
}
alert(ajaxObj.error);
return false;
}
alert('[' + error_num + '] Something went wrong.');
return false;
}, function() {
if (!hide_indicator) {
hideIndicator();
}
alert('[' + error_num + '] Something went wrong.');
return false;
});
}
Here, a .then
function can be used to pre-process the response, and return the appropriate value to the subsequent promise handlers.
A subsequent promise handler can be implemented as follows, which calls the doAjax
function, and reacts on the value returns by pre-processing.
$(document).on('click', '.ad-single', function() {
doAjax('post_requests', 'get-ad-info', {"id": $(this).data('ad')}, '158', false)
.then(function(addataObj) {
if (addataObj) {
loadContent(addataObj.ad);
}
});
});
Here, the .then()
chains up with the previous .then()
, and reacts on the value returned (which is received in addataObj
). Note that it only reacts on the success of the AJAX call. If you want to handle the error response as well, you need to pass a second argument as a function to the .then()
handler.
Hope this solves your problem.
Upvotes: 5