使用 jQuery 在失败时重试 AJAX 请求的最佳方法是什么?

伪代码:

$(document).ajaxError(function(e, xhr, options, error) {
xhr.retry()
})

更好的办法是做出指数级的让步

111001 次浏览

就像这样:


$.ajax({
url : 'someurl',
type : 'POST',
data :  ....,
tryCount : 0,
retryLimit : 3,
success : function(json) {
//do something
},
error : function(xhr, textStatus, errorThrown ) {
if (textStatus == 'timeout') {
this.tryCount++;
if (this.tryCount <= this.retryLimit) {
//try again
$.ajax(this);
return;
}
return;
}
if (xhr.status == 500) {
//handle error
} else {
//handle error
}
}
});

我在下面的代码中取得了很大的成功(例如: http://jsfiddle.net/uZSFK/)

$.ajaxSetup({
timeout: 3000,
retryAfter:7000
});


function func( param ){
$.ajax( 'http://www.example.com/' )
.success( function() {
console.log( 'Ajax request worked' );
})
.error(function() {
console.log( 'Ajax request failed...' );
setTimeout ( function(){ func( param ) }, $.ajaxSetup().retryAfter );
});
}

这里有一个小插件:

Https://github.com/execjosh/jquery-ajax-retry

Auto incrementing timeout would be a good addition to it.

要在全局范围内使用它,只需使用 $创建自己的函数。Ajax 签名,使用那里的重试 api 并替换所有 $。通过新函数调用 ajax。

您也可以直接替换 $. ajax,但是如果不重试,您将无法进行 xhr 调用。

下面是我使用的异步加载库的方法:

var jqOnError = function(xhr, textStatus, errorThrown ) {
if (typeof this.tryCount !== "number") {
this.tryCount = 1;
}
if (textStatus === 'timeout') {
if (this.tryCount < 3) {  /* hardcoded number */
this.tryCount++;
//try again
$.ajax(this);
return;
}
return;
}
if (xhr.status === 500) {
//handle error
} else {
//handle error
}
};


jQuery.loadScript = function (name, url, callback) {
if(jQuery[name]){
callback;
} else {
jQuery.ajax({
name: name,
url: url,
dataType: 'script',
success: callback,
async: true,
timeout: 5000, /* hardcoded number (5 sec) */
error : jqOnError
});
}
}

然后从你的应用程序中调用 .load_script并嵌套你的成功回调函数:

$.loadScript('maps', '//maps.google.com/maps/api/js?v=3.23&libraries=geometry&libraries=places&language=&hl=&region=', function(){
initialize_map();
loadListeners();
});

DemoUsers's answer doesn't work with Zepto, since this in the error function is pointing to Window. (And that way of using 'this' is not secure enough as you don't know how they implement ajax or no need to.)

对于 Zepto,也许你可以试试下面的方法,直到现在它对我还是很有效的:

var AjaxRetry = function(retryLimit) {
this.retryLimit = typeof retryLimit === 'number' ? retryLimit : 0;
this.tryCount = 0;
this.params = null;
};
AjaxRetry.prototype.request = function(params, errorCallback) {
this.tryCount = 0;
var self = this;
params.error = function(xhr, textStatus, error) {
if (textStatus === 'timeout') {
self.tryCount ++;
if (self.tryCount <= self.retryLimit) {
$.ajax(self.params)
return;
}
}
errorCallback && errorCallback(xhr, textStatus, error);
};
this.params = params;
$.ajax(this.params);
};
//send an ajax request
new AjaxRetry(2).request(params, function(){});

使用构造函数确保请求是可重入的!

One approach is to use a wrapper function:

(function runAjax(retries, delay){
delay = delay || 1000;
$.ajax({
type        : 'GET',
url         : '',
dataType    : 'json',
contentType : 'application/json'
})
.fail(function(){
console.log(retries); // prrint retry count
retries > 0 && setTimeout(function(){
runAjax(--retries);
},delay);
})
})(3, 100);

另一种方法是在 $.ajax上使用 retries属性

// define ajax settings
var ajaxSettings = {
type        : 'GET',
url         : '',
dataType    : 'json',
contentType : 'application/json',
retries     : 3  //                 <-----------------------
};


// run initial ajax
$.ajax(ajaxSettings).fail(onFail)


// on fail, retry by creating a new Ajax deferred
function onFail(){
if( ajaxSettings.retries-- > 0 )
setTimeout(function(){
$.ajax(ajaxSettings).fail(onFail);
}, 1000);
}

另一种方法(GIST 要点)-覆盖原始 $.ajax(更适合 DRY)

// enhance the original "$.ajax" with a retry mechanism
$.ajax = (($oldAjax) => {
// on fail, retry by creating a new Ajax deferred
function check(a,b,c){
var shouldRetry = b != 'success' && b != 'parsererror';
if( shouldRetry && --this.retries > 0 )
setTimeout(() => { $.ajax(this) }, this.retryInterval || 100);
}


return settings => $oldAjax(settings).always(check)
})($.ajax);






// now we can use the "retries" property if we need to retry on fail
$.ajax({
type          : 'GET',
url           : 'http://www.whatever123.gov',
timeout       : 2000,
retries       : 3,     //       <-------- Optional
retryInterval : 2000   //       <-------- Optional
})
// Problem: "fail" will only be called once, and not for each retry
.fail(()=>{
console.log('failed')
});

需要考虑的一点是使 当然先前没有包装 $.ajax方法,以避免相同的代码运行两次。


您可以将这些代码段复制粘贴到控制台以测试它们

如果有人在 Ajax 调用之后调用 .done(),那么这些答案都不起作用,因为您将没有成功的方法附加到未来的回调中。所以如果有人这么做:

$.ajax({...someoptions...}).done(mySuccessFunc);

那么重试时 mySuccessFunc就不会被调用。以下是我的解决方案,它大量借用了@cjpak 的答案 here。在我的示例中,当 AWS 的 API 网关响应为502错误时,我想重试。

const RETRY_WAIT = [10 * 1000, 5 * 1000, 2 * 1000];


// This is what tells JQuery to retry $.ajax requests
// Ideas for this borrowed from https://stackoverflow.com/a/12446363/491553
$.ajaxPrefilter(function(opts, originalOpts, jqXHR) {
if(opts.retryCount === undefined) {
opts.retryCount = 3;
}


// Our own deferred object to handle done/fail callbacks
let dfd = $.Deferred();


// If the request works, return normally
jqXHR.done(dfd.resolve);


// If the request fails, retry a few times, yet still resolve
jqXHR.fail((xhr, textStatus, errorThrown) => {
console.log("Caught error: " + JSON.stringify(xhr) + ", textStatus: " + textStatus + ", errorThrown: " + errorThrown);
if (xhr && xhr.readyState === 0 && xhr.status === 0 && xhr.statusText === "error") {
// API Gateway gave up.  Let's retry.
if (opts.retryCount-- > 0) {
let retryWait = RETRY_WAIT[opts.retryCount];
console.log("Retrying after waiting " + retryWait + " ms...");
setTimeout(() => {
// Retry with a copied originalOpts with retryCount.
let newOpts = $.extend({}, originalOpts, {
retryCount: opts.retryCount
});
$.ajax(newOpts).done(dfd.resolve);
}, retryWait);
} else {
alert("Cannot reach the server.  Please check your internet connection and then try again.");
}
} else {
defaultFailFunction(xhr, textStatus, errorThrown); // or you could call dfd.reject if your users call $.ajax().fail()
}
});


// NOW override the jqXHR's promise functions with our deferred
return dfd.promise(jqXHR);
});

此代码片段将在2秒、5秒、10秒后退出并重试,您可以通过修改 RETRY _ WAIT 常量来编辑这些代码片段。

AWS support suggested we add a retry, since it happens for us only once in a blue moon.

您的代码几乎已经满了:)

const counter = 0;
$(document).ajaxSuccess(function ( event, xhr, settings ) {
counter = 0;
}).ajaxError(function ( event, jqxhr, settings, thrownError ) {
if (counter === 0 /*any thing else you want to check ie && jqxhr.status === 401*/) {
++counter;
$.ajax(settings);
}
});