wake-up-neo.net

Was ist der beste Weg, um erneut zu versuchen AJAX Anfrage bei Fehler mit jQuery?

Pseudo-Code:

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

Noch besser wäre eine Art exponentielles Backoff

88
Tom Lehman

Etwas wie das:


$.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
        }
    }
});
202

Ein Ansatz ist die Verwendung einer Wrapper-Funktion:

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

Ein anderer Ansatz wäre die Verwendung einer retries-Eigenschaft für den $.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);
}

Eine andere Möglichkeit ( Gist ) - überschreibt den ursprünglichen $.ajax (besser für 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') 
});

Zu bedenken ist, dass sure die $.ajax-Methode zuvor nicht bereits umbrochen wurde, um zu vermeiden, dass derselbe Code zweimal ausgeführt wird.


Sie können diese Snippets (so wie sie sind) in die Konsole kopieren und einfügen, um sie zu testen

7
vsync

Ich habe mit diesem Code viel Erfolg gehabt (Beispiel: 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
Nabil Kadimi

Die Antwort von DemoUsers funktioniert nicht mit Zepto, da dies in der Fehlerfunktion auf Window zeigt. (Und diese Art der Verwendung von 'this' ist nicht sicher genug, da Sie nicht wissen, wie sie Ajax implementieren oder nicht.)

Für Zepto könnten Sie vielleicht unten versuchen, bis jetzt funktioniert es gut für mich:

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

Verwenden Sie den Konstruktor, um sicherzustellen, dass die Anforderung wiedereintrittsfähig ist.

0
Xhua

Hier ist ein kleines Plugin dafür:

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

Auto-Inkrementierungs-Timeout wäre eine gute Ergänzung.

Um es global zu verwenden, erstellen Sie einfach Ihre eigene Funktion mit der $ .ajax-Signatur, verwenden Sie dort den api erneut und ersetzen Sie alle Ihre $ .ajax-Aufrufe durch Ihre neue Funktion.

Sie können $ .ajax auch direkt ersetzen, aber Sie können dann keine Xhr-Aufrufe ohne Wiederholung durchführen.

0
Oleg Isonen

Hier ist die Methode, die für mich beim asynchronen Laden von Bibliotheken funktioniert:

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

Dann rufen Sie einfach .load_script von Ihrer App auf und verschachteln Ihren Erfolgsrückruf:

$.loadScript('maps', '//maps.google.com/maps/api/js?v=3.23&libraries=geometry&libraries=places&language=&hl=&region=', function(){
    initialize_map();
    loadListeners();
});
0
Abram

Keine dieser Antworten funktioniert, wenn jemand nach seinem Ajax-Aufruf .done() aufruft, weil Sie keine Erfolgsmethode für den zukünftigen Rückruf haben. Also, wenn jemand dies tut:

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

Dann wird mySuccessFunc bei der Wiederholung nicht aufgerufen. Hier ist meine Lösung, die stark von @ cjpaks Antwort hier entlehnt ist. In meinem Fall möchte ich es erneut versuchen, wenn das API-Gateway von AWS mit dem Fehler 502 antwortet. 

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

Dieses Snippet wird nach 2 Sekunden, dann 5 Sekunden und 10 Sekunden zurückgesetzt und erneut versucht. Sie können es bearbeiten, indem Sie die RETRY_WAIT-Konstante ändern.

Die AWS-Unterstützung schlug vor, einen Wiederholungsversuch hinzuzufügen, da dies für uns nur einmal in einem blauen Mond geschieht.

0