我正在尝试实现一个重试由于临时原因而失败的ajax请求的系统。在我的情况下,这是关于重试失败的请求,该请求带有401状态代码,因为会话已过期,然后调用了刷新会话的刷新Web服务。
问题在于成功完成重试后不会调用“完成”回调,这与调用“成功” 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拒绝的请求排队,并在成功调用/ 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 }});
该机制有效,第二次触发401失败的请求,问题是未调用其“完成”回调,因此应用程序停顿了。
您可以jQuery.ajaxPrefilter用来将jqXHR包装在另一个延迟的对象中。
jQuery.ajaxPrefilter
我在上面的示例jsFiddle中显示了它的工作原理,并尝试调整您的一些代码以将401处理到此版本中:
jsFiddle
$.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({}, originalOpts, { 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上的所有“承诺方法”。
deferred.promise(object)
注意: 对于任何其他发现此问题的人,如果您在ajax选项中使用success:和附加回调error:,则此代码段将 无法 按您期望的方式工作。假定唯一的回调是使用jqXHR 的.done(callback)和.fail(callback)方法附加的回调。
success:
error:
.done(callback)
.fail(callback)