I’m trying to implement a system of retrying ajax requests that fail for a temporary reason. In my case, it is about retrying requests that failed with a 401 status code because the session has expired, after calling a refresh webservice that revives the session.
The problem is that the “done” callbacks are not called on a successful retry, unlike the “success” ajax option callback that is called. I’ve made up a simple example below:
$.ajaxSetup({statusCode: {
404: function() {
this.url = '/existent_url';
$.ajax(this);
}
}});
$.ajax({
url: '/inexistent_url',
success: function() { alert('success'); }
})
.done(function() {
alert('done');
});
Is there a way to have done-style callbacks called on a successful retry? I know a deferred can’t be ‘resolved’ after it was ‘rejected’, is it possible to prevent the reject? Or maybe copy the doneList of the original deferred to a new deferred? I’m out of ideas:)
A more realistic example below, where I’m trying to queue up all 401-rejected requests, and retry them after a successful call to /refresh.
var refreshRequest = null,
waitingRequests = null;
var expiredTokenHandler = function(xhr, textStatus, errorThrown) {
//only the first rejected request will fire up the /refresh call
if(!refreshRequest) {
waitingRequests = $.Deferred();
refreshRequest = $.ajax({
url: '/refresh',
success: function(data) {
// session refreshed, good
refreshRequest = null;
waitingRequests.resolve();
},
error: function(data) {
// session can't be saved
waitingRequests.reject();
alert('Your session has expired. Sorry.');
}
});
}
// put the current request into the waiting queue
(function(request) {
waitingRequests.done(function() {
// retry the request
$.ajax(request);
});
})(this);
}
$.ajaxSetup({statusCode: {
401: expiredTokenHandler
}});
The mechanism works, the 401-failed requests get fired a second time, the problem is their ‘done’ callbacks do not get called, so the applications stalls.
You could use
jQuery.ajaxPrefilterto wrap the jqXHR in another deferred object.I made an example on
jsFiddlethat shows it working, and tried to adapt some of your code to handle the 401 into this version:This works because
deferred.promise(object)will actually overwrite all of the “promise methods” on the jqXHR.NOTE: To anyone else finding this, if you are attaching callbacks with
success:anderror:in the ajax options, this snippet will not work the way you expect. It assumes that the only callbacks are the ones attached using the.done(callback)and.fail(callback)methods of the jqXHR.