我试图实现一个系统重试ajax请求失败的临时原因。在我的情况下,它是关于重试请求失败的401状态代码,因为会话已过期,在调用刷新webservice恢复会话后。
问题是,“done”回调不会在成功的重试上调用,与调用的“success”ajax选项回调不同。我已经做了一个简单的例子如下:
$.ajaxSetup({statusCode: { 404: function() { this.url = '/existent_url'; $.ajax(this); } }}); $.ajax({ url: '/inexistent_url',success: function() { alert('success'); } }) .done(function() { alert('done'); });
有没有一种方法来完成样式的回调调用成功的重试?我知道一个推迟的人在“被拒绝”之后不能被“解决”,是否有可能防止被拒绝?或者也许复制原始延迟的doneList到新的延迟?我的想法:)
一个更现实的示例如下,其中我试图排队所有401拒绝的请求,并在成功调用/刷新后重试它们。
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 }});
机制工作,401失败的请求第二次被触发,问题是他们的“完成”回调没有被调用,所以应用程序停顿。
解决方法
你可以使用
jQuery.ajaxPrefilter
将jqXHR包装在另一个
deferred object。
我做了一个例子,jsFiddle
显示它的工作,并试图适应一些你的代码来处理401到这个版本:
$.ajaxPrefilter(function(opts,originalOpts,jqXHR) { // you could pass this option in on a "retry" so that it doesn't // get all recursive on you. if (opts.refreshRequest) { return; } // our own deferred object to handle done/fail callbacks var dfd = $.Deferred(); // if the request works,return normally jqXHR.done(dfd.resolve); // if the request fails,do something else // yet still resolve jqXHR.fail(function() { var args = Array.prototype.slice.call(arguments); if (jqXHR.status === 401) { $.ajax({ url: '/refresh',refreshRequest: true,error: function() { // session can't be saved alert('Your session has expired. Sorry.'); // reject with the original 401 data dfd.rejectWith(jqXHR,args); },success: function() { // retry with a copied originalOpts with refreshRequest. var newOpts = $.extend({},{ refreshRequest: true }); // pass this one on to our deferred pass or fail. $.ajax(newOpts).then(dfd.resolve,dfd.reject); } }); } else { dfd.rejectWith(jqXHR,args); } }); // NOW override the jqXHR's promise functions with our deferred return dfd.promise(jqXHR); });
这是因为deferred.promise(object)
实际上会覆盖jqXHR上的所有“promise方法”。
注意:任何人发现这一点,如果你附加回调成功:和错误:在ajax选项,这个片段将无法按预期的方式工作。它假定唯一的回调是使用jqXHR的.done(callback)和.fail(callback)方法附加的回调。