Javascript 使用 jQuery 在失败时重试 AJAX 请求的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10024469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's the best way to retry an AJAX request on failure using jQuery?
提问by Tom Lehman
Pseudo code:
伪代码:
$(document).ajaxError(function(e, xhr, options, error) {
xhr.retry()
})
Even better would be some kind of exponential back-off
更好的是某种指数退避
回答by Sudhir Bastakoti
Something like this:
像这样的东西:
$.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
}
}
});
回答by vsync
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);
Another approach would be to use a retries
property on the $.ajax
另一种方法是retries
在$.ajax
// 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);
}
Another way (GIST) - override original $.ajax
(better for DRY)
另一种方式(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')
});
A point to consider is making surethe $.ajax
method wasn't already wrapped previously, in order to avoid the same code running twice.
需要考虑的一点是确保该$.ajax
方法之前没有被包装,以避免相同的代码运行两次。
You can copy-paste these snippets (as-is) to the console to test them
您可以将这些片段(按原样)复制粘贴到控制台以进行测试
回答by Nabil Kadimi
I've had a lot of success with this code below (example: http://jsfiddle.net/uZSFK/)
我在下面的代码中取得了很多成功(例如: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 );
});
}
回答by Ryan Shillington
None of these answers work if somebody calls .done()
after their ajax call because you won't have the success method to attach to the future call back. So if somebody does this:
如果有人.done()
在他们的 ajax调用之后调用这些答案,这些答案都不起作用,因为您将没有成功方法来附加到未来的回调。所以如果有人这样做:
$.ajax({...someoptions...}).done(mySuccessFunc);
Then mySuccessFunc
won't get called on the retry. Here's my solution, which is heavily borrowed from @cjpak's answer here. In my case I want to retry when AWS's API Gateway responds with 502 error.
然后mySuccessFunc
在重试时不会被调用。这里是我的解决方案,这在很大程度上来自@ cjpak的答案借这里。就我而言,我想在 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);
});
This snippet will back-off and retry after 2 seconds, then 5 seconds, then 10 seconds, which you can edit by modifying the RETRY_WAIT constant.
此代码段将在 2 秒、5 秒和 10 秒后退避并重试,您可以通过修改 RETRY_WAIT 常量进行编辑。
AWS support suggested we add a retry, since it happens for us only once in a blue moon.
AWS 支持建议我们添加重试,因为它只会在蓝月亮中发生一次。
回答by Andriy
Your code is almost full :)
您的代码几乎已满:)
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);
}
});
回答by Oleg Isonen
Here is a small plugin for this:
这是一个小插件:
https://github.com/execjosh/jquery-ajax-retry
https://github.com/execjosh/jquery-ajax-retry
Auto incrementing timeout would be a good addition to it.
自动递增超时将是一个很好的补充。
To use it globally just create your own function with $.ajax signature, use there retry api and replace all your $.ajax calls by your new function.
要在全局范围内使用它,只需使用 $.ajax 签名创建您自己的函数,在那里使用 retry api 并用您的新函数替换所有 $.ajax 调用。
Also you could directly replace $.ajax, but you will not be able to make xhr calls without retry then.
您也可以直接替换 $.ajax,但是如果不重试,您将无法进行 xhr 调用。
回答by Abram
Here's the method that worked for me for asynchronous loading of libraries:
这是对我有用的异步加载库的方法:
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
});
}
}
Then just call .load_script
from your app and nest your success callback:
然后只需.load_script
从您的应用程序调用并嵌套您的成功回调:
$.loadScript('maps', '//maps.google.com/maps/api/js?v=3.23&libraries=geometry&libraries=places&language=&hl=®ion=', function(){
initialize_map();
loadListeners();
});
回答by Xhua
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.)
DemoUsers 的答案不适用于 Zepto,因为错误函数中的 this 指向 Window。(这种使用 'this' 的方式不够安全,因为您不知道它们如何实现 ajax 或不需要。)
For Zepto, maybe you could try below, till now it works well for me:
对于 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(){});
Use constructor to make sure request is reentrant!
使用构造函数来确保请求是可重入的!