Pseudo code:
$(document).ajaxError(function(e, xhr, options, error) {
xhr.retry()
})
Encore mieux serait une sorte de recul exponentiel
Pseudo code:
$(document).ajaxError(function(e, xhr, options, error) {
xhr.retry()
})
Encore mieux serait une sorte de recul exponentiel
Réponses:
Quelque chose comme ça:
$.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
est attaché à l'appel de la fonction qui renvoie cette requête ajax?
tryCount
et retryLimit
est excessif. Envisagez d'utiliser seulement 1 variable: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
propriété sur le$.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
(mieux pour 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')
});
Un point à considérer est de s'assurer que la $.ajax
méthode n'a pas déjà été encapsulée précédemment, afin d'éviter que le même code ne s'exécute deux fois.
Vous pouvez copier-coller ces extraits (tels quels) dans la console pour les tester
J'ai eu beaucoup de succès avec ce code ci-dessous (exemple: 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 );
});
}
Aucune de ces réponses ne fonctionne si quelqu'un appelle .done()
après son appel ajax car vous n'aurez pas la méthode de réussite pour vous attacher au futur rappel. Donc, si quelqu'un fait ça:
$.ajax({...someoptions...}).done(mySuccessFunc);
Ensuite mySuccessFunc
, vous ne serez pas appelé lors de la nouvelle tentative. Voici ma solution, qui est largement empruntée à la réponse de @ cjpak ici . Dans mon cas, je souhaite réessayer lorsque la passerelle API d'AWS répond avec une erreur 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);
});
Cet extrait de code s'arrêtera et réessayera après 2 secondes, puis 5 secondes, puis 10 secondes, que vous pouvez modifier en modifiant la constante RETRY_WAIT.
Le support AWS nous a suggéré d'ajouter une nouvelle tentative, car cela ne se produit qu'une seule fois dans une lune bleue.
Voici un petit plugin pour cela:
https://github.com/execjosh/jquery-ajax-retry
Le délai d'incrémentation automatique serait un bon ajout à cela.
Pour l'utiliser globalement, créez simplement votre propre fonction avec la signature $ .ajax, utilisez-la pour réessayer api et remplacez tous vos appels $ .ajax par votre nouvelle fonction.
Vous pouvez également remplacer directement $ .ajax, mais vous ne pourrez pas faire d'appels xhr sans réessayer.
Voici la méthode qui a fonctionné pour moi pour le chargement asynchrone des bibliothèques:
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
});
}
}
Ensuite, appelez simplement .load_script
depuis votre application et imbriquez votre rappel de réussite:
$.loadScript('maps', '//maps.google.com/maps/api/js?v=3.23&libraries=geometry&libraries=places&language=&hl=®ion=', function(){
initialize_map();
loadListeners();
});
La réponse de DemoUsers ne fonctionne pas avec Zepto, car cela dans la fonction d'erreur pointe vers Window. (Et cette façon d'utiliser 'this' n'est pas suffisamment sécurisée car vous ne savez pas comment ils implémentent ajax ou pas besoin de le faire.)
Pour Zepto, vous pourriez peut-être essayer ci-dessous, jusqu'à présent, cela fonctionne bien pour moi:
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(){});
Utilisez le constructeur pour vous assurer que la requête est réentrante!
Votre code est presque plein :)
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
, et en cas d'échec, vous appelez votre fonction avectries+1
. Arrêtez l'exécution surtries==3
ou tout autre numéro.