[英]What's the best way to retry an AJAX request on failure using jQuery?
偽代碼:
$(document).ajaxError(function(e, xhr, options, error) {
xhr.retry()
})
更好的是某種指數退避
像這樣:
$.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
}
}
});
(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);
}
$.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
方法,以避免相同的代碼運行兩次。
您可以將這些片段(按原樣)復制粘貼到控制台以測試它們
我在下面的代碼中取得了很大的成功(例如: 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 );
});
}
如果有人在他們的 ajax 電話之后調用.done()
,那么這些答案都不起作用,因為您將沒有附加到未來回調的成功方法。 所以如果有人這樣做:
$.ajax({...someoptions...}).done(mySuccessFunc);
然后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);
});
此代碼段將在 2 秒、5 秒、10 秒后退出並重試,您可以通過修改 RETRY_WAIT 常量對其進行編輯。
AWS 支持人員建議我們添加重試,因為它對我們來說只發生一次在藍色月亮上。
你的代碼快滿了:)
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);
}
});
這是一個小插件:
https://github.com/execjosh/jquery-ajax-retry
自動遞增超時將是一個很好的補充。
要在全球范圍內使用它,只需創建您自己的帶有 $.ajax 簽名的 function,然后在那里重試 api 並將所有 $.ajax 調用替換為新的 function。
您也可以直接替換 $.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=®ion=', function(){
initialize_map();
loadListeners();
});
DemoUsers 的回答不適用於 Zepto,因為錯誤 function 中的 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(){});
使用構造函數來確保請求是可重入的!
我用@vsync 3rd code解決了我的具體問題。
$.ajax = (($oldAjax) => {
var df = $.Deferred();
// on fail, retry by creating a new Ajax deferred
function check(self, status) {
console.log("check " + status + " => " + self.retries);
const shouldRetry = status != 'success' && status != 'parsererror';
if (shouldRetry && self.retries > 0) {
setTimeout(() => {
console.log("retry " + self.retries);
$.ajax(self);
}, self.retryInterval || 100);
}
}
function failed(jqXHR, status, e) {
if (this.retries - 1 <= 0) {
// 재시도 횟수가 끝나면, 오류 보내기
df.reject(KfError.convertKfError(jqXHR, this.url));
} else {
this.retries --;
check(this, 'retry', this.retries);
}
}
function done(res, textStatus, jqXHR) {
if (!res.success) { // 200 코드이지만, 응답에 실패라면 오류로 처리
if (this.retries - 1 <= 0) {
df.reject(KfError.createResponseError(res, this.url));
} else {
this.retries --;
check(this, 'retry', this.retries)
}
} else {
df.resolve(res, textStatus, jqXHR);
}
}
return function (settings) {
$oldAjax(settings)
.fail(failed)
.done(done);
return df;
};
})($.ajax);
function createRequest(url) {
return $.ajax({
type: 'GET',
url: url,
timeout: 2000,
retries: 3,
retryInterval: 1000
});
}
$(function () {
createRequest(Rest.correctUrl('/auth/refres'))
.then((res) => {
console.log('ok res');
})
.catch((e) => {
// Finally catch error after retrial.
console.log(e);
});
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.