使用 jQuery 在失败时重试 AJAX 请求的最佳方法是啥?
Posted
技术标签:
【中文标题】使用 jQuery 在失败时重试 AJAX 请求的最佳方法是啥?【英文标题】:What's the best way to retry an AJAX request on failure using jQuery?使用 jQuery 在失败时重试 AJAX 请求的最佳方法是什么? 【发布时间】:2012-04-18 22:46:40 【问题描述】:伪代码:
$(document).ajaxError(function(e, xhr, options, error)
xhr.retry()
)
更好的是某种指数回退
【问题讨论】:
我不确定这是否是最好的方法,所以只是一个评论,但如果你从一个函数调用你的ajax,你可以给它一个参数tries
,如果失败你用tries+1
调用你的函数。在tries==3
或任何其他号码上停止执行。
Retry a jquery ajax request which has callbacks attached to its deferred 的可能重复项
不错的解决方案:***.com/questions/6298612/…
【参考方案1】:
类似这样的:
$.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
);
【讨论】:
我采用了@Sudhir 的解决方案,并在 github 上创建了一个 $.retryAjax 插件:github.com/mberkom/jQuery.retryAjax 这对我不起作用。条件中的 this.tryCount 始终为 1。 @MichaelBerkompas - 你的插件还能用吗?它在 2 年内没有收到提交。 如果将另一个回调处理程序(如.success
)附加到调用返回此 ajax 请求的函数,这是否可行?
tryCount
和 retryLimit
对过多。考虑只使用 1 个变量:this.retryLimit--; if (this.retryLimit) ... $.ajax(this) ...
【参考方案2】:
一种方法是使用包装函数:
(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
方法之前没有被包装,以避免相同的代码运行两次。
您可以将这些 sn-ps(按原样)复制粘贴到控制台以进行测试
【讨论】:
感谢脚本。它可以与 $.ajaxSetup 一起使用吗? @SevbanÖztürk - 你是什么意思?试试看:) 感谢您教我如何构建包装器!这击败了我过去实现的旧递归函数设计。【参考方案3】:我在下面的代码中取得了很大的成功(例如: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 );
);
【讨论】:
我建议的唯一更改是将 'func("'+param"'")' 替换为 function()func(param)。这样,您可以直接传递参数无需将其转换为字符串并返回,这很容易失败! @fabspro 完成。谢谢! 这不是死循环吗?鉴于这个问题有一个 retryLimit 并且显然想要迎合服务器永远不会回来......我认为这真的必须在那里 jQuery.ajaxSetup() 描述:为未来的 Ajax 请求设置默认值。不推荐使用它。 api.jquery.com/jQuery.ajaxSetup【参考方案4】:如果有人在他们的 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://***.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);
);
此 sn-p 将在 2 秒、5 秒、10 秒后回退并重试,您可以通过修改 RETRY_WAIT 常量来编辑。
AWS 支持建议我们添加一次重试,因为它在我们身上只会发生一次。
【讨论】:
我发现这是迄今为止所有答案中最有用的。但是,最后一行阻止了 TypeScript 中的编译。我认为您不应该从此函数返回任何内容。【参考方案5】:你的代码快满了 :)
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);
);
【讨论】:
【参考方案6】:这是一个小插件:
https://github.com/execjosh/jquery-ajax-retry
自动递增超时将是一个很好的补充。
要在全局范围内使用它,只需使用 $.ajax 签名创建您自己的函数,使用那里的 retry api 并将您的所有 $.ajax 调用替换为您的新函数。
你也可以直接替换$.ajax,但是如果不重试,你将无法进行xhr调用。
【讨论】:
【参考方案7】:这是对我有用的异步加载库的方法:
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=®ion=', function()
initialize_map();
loadListeners();
);
【讨论】:
【参考方案8】:DemoUsers 的答案不适用于 Zepto,因为错误函数中的 this 指向 Window。 (而且使用 'this' 的方式不够安全,因为您不知道它们如何实现 ajax 或不需要。)
对于 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());
使用构造函数确保请求是可重入的!
【讨论】:
以上是关于使用 jQuery 在失败时重试 AJAX 请求的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
tenacity发生异常/失败/错误时重试retry机制,Python
tenacity发生异常/失败/错误时重试retry机制,Python