Псевдокод:
$(document).ajaxError(function(e, xhr, options, error) {
xhr.retry()
})
Еще лучше было бы какое-то экспоненциальное отставание
Псевдокод:
$(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
}
}
});
.success
, прикреплен к вызову функции, которая возвращает этот запрос ajax?
tryCount
и retryLimit
излишне. Подумайте об использовании только 1 переменной:this.retryLimit--; if (this.retryLimit) { ... $.ajax(this) ... }
(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);
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);
}
$.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 );
});
}
Ни один из этих ответов не сработает, если кто-то позвонит .done()
после своего вызова ajax, потому что у вас не будет метода успеха для присоединения к будущему обратному вызову. Итак, если кто-то сделает это:
$.ajax({...someoptions...}).done(mySuccessFunc);
Тогда mySuccessFunc
не будет вызова при повторной попытке. Вот мое решение, которое в значительной степени заимствовано из ответа @cjpak здесь . В моем случае я хочу повторить попытку, когда AWS API Gateway отвечает с ошибкой 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 предложила добавить повторную попытку, так как это происходит у нас только один раз в синюю луну.
Вот небольшой плагин для этого:
https://github.com/execjosh/jquery-ajax-retry
Автоматическое увеличение тайм-аута было бы хорошим дополнением к этому.
Чтобы использовать его глобально, просто создайте свою собственную функцию с подписью $ .ajax, используйте там retry 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=®ion=', function(){
initialize_map();
loadListeners();
});
Ответ DemoUsers не работает с Zepto, так как это в функции ошибки указывает на 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(){});
Используйте конструктор, чтобы убедиться, что запрос повторяется!
Ваш код почти заполнен :)
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);
}
});
tries
, а в случае неудачи вы вызовете свою функцию с помощьюtries+1
. Остановить выполнение наtries==3
любом другом номере.