Quelle est la meilleure façon de rejouer une requête AJAX sur l'échec en utilisant jQuery?

Pseudo code:

$(document).ajaxError(function(e, xhr, options, error) {
  xhr.retry()
})

encore mieux serait une sorte de recul exponentiel

86
demandé sur Sergio Tulentsev 2012-04-05 11:38:26

7 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
        }
    }
});
196
répondu Sudhir Bastakoti 2012-04-05 07:44:22

L'une des approches consiste à utiliser une fonction d'enrubannage:

(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);

une autre approche serait d'utiliser une propriété retries 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);
}

une Autre façon ( GIST ) - remplacer l'original $.ajax (plus SEC)

// 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 faire sûr la méthode $.ajax n'était pas déjà enveloppé auparavant, afin de évitez que le même code ne s'exécute deux fois.


vous pouvez copier-coller ces extraits (tels quels) sur la console pour les tester

6
répondu vsync 2017-03-06 10:30:38

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 );
        });
}
4
répondu Nabil Kadimi 2014-10-17 06:53:10

voici un petit plugin pour cela:

https://github.com/execjosh/jquery-ajax-retry

Auto-incrémentation de délai d'attente serait un bon ajout.

pour l'utiliser Globalement, il suffit de créer votre propre fonction avec $.signature ajax, utilisez ici l'api de Rety et remplacez tous vos $.les appels ajax par votre nouvelle fonction.

vous pouvez aussi remplacer directement $.ajax, mais vous ne serez pas en mesure faire des appels xhr sans réessayer alors.

0
répondu Oleg Isonen 2013-05-23 13:10:35

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
    });
  }
}

alors il suffit d'appeler .load_script à partir de votre application et emboîter votre succès callback:

$.loadScript('maps', '//maps.google.com/maps/api/js?v=3.23&libraries=geometry&libraries=places&language=&hl=&region=', function(){
    initialize_map();
    loadListeners();
});
0
répondu Abram 2016-03-09 00:36:01

la réponse de DemoUsers ne fonctionne pas avec Zepto, puisque cela dans la fonction d'erreur pointe vers Window. (Et cette façon d'utiliser 'ceci' n'est pas assez sécurisée car vous ne savez pas comment ils implémentent ajax ou pas besoin de le faire.)

pour Zepto, peut-être pourriez-vous essayer ci-dessous, jusqu'à maintenant 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 request est reentrant!

0
répondu Xhua 2016-05-25 11:21:35

aucune de ces réponses ne fonctionne si quelqu'un appelle .done() après son appel ajax parce que vous n'aurez pas la méthode de succès à attacher au futur rappel. Donc, si quelqu'un fait cela:

$.ajax({...someoptions...}).done(mySuccessFunc);

puis mySuccessFunc ne seront pas appelés sur le retour. Voici ma solution, qui est largement empruntée à la réponse de @cjpak ici . Dans mon cas, je veux 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);
});

ce fragment recule Et recommence après 2 secondes, puis 5 secondes, puis 10 Secondes, que vous pouvez éditer en modifiant la constante RETRY_WAIT.

soutien AWS suggéré que nous ajoutons un retentissement, car il se produit pour nous qu'une seule fois dans une lune bleue.

0
répondu Ryan Shillington 2018-06-20 17:15:23