JQuery kullanarak bir AJAX isteğini hata durumunda yeniden denemenin en iyi yolu nedir?


107

Sözde kod:

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

Daha da iyisi, bir tür üstel geri çekilme olurdu


1
Bunun en iyi yol olup olmadığından emin değilim, bu yüzden sadece bir yorum, ancak ajax'ınızı bir triesişlevden çağırırsanız, ona bir parametre verebilir ve başarısız olursa işlevinizi çağırırsınız tries+1. Yürütmeyi tries==3veya başka bir numarayı durdurun .
Nanne


Yanıtlar:


238

Bunun gibi bir şey:


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

12
@ Sudhir'in çözümünü aldım ve github'da bir $ .retryAjax eklentisi oluşturdum: github.com/mberkom/jQuery.retryAjax
Michael Berkompas

2
Bu benim için çalışmıyor. Koşullu this.tryCount her zaman 1'dir.
user304602

2
@MichaelBerkompas - eklentiniz hala çalışıyor mu? 2 yıldır taahhüt almadı.
Hendrik

2
.successBu ajax isteğini döndüren işlevi çağırmaya benzer başka bir geri arama işleyicisi eklenmişse bu çalışacak mı?
ProblemsOfSumit

17
Çift tryCountve retryLimitaşırı. Yalnızca 1 değişken kullanmayı düşünün:this.retryLimit--; if (this.retryLimit) { ... $.ajax(this) ... }
vladkras

15

Bir yaklaşım, bir sarmalayıcı işlevi kullanmaktır:

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

Diğer bir yaklaşım, bir retriesmülkü kullanmaktır .$.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);
}

Başka bir yol ( GIST ) - orijinali geçersiz kılma $.ajax(KURU için daha iyi)

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

Bir nokta yapıyor düşünün emin$.ajax iki kez çalıştıran aynı kod önlemek için, yöntem zaten önceden sarılmış değildi.


Bu snippet'leri (olduğu gibi) test etmek için konsola kopyalayıp yapıştırabilirsiniz.


Senaryo için teşekkürler. $ .AjaxSetup ile çalışıyor mu?
Sevban Öztürk

@ SevbanÖztürk - Ne demek istiyorsun? sadece dene :)
vsync

Bir sarmalayıcıyı nasıl yapılandıracağımı öğrettiğin için teşekkürler! Bu, uygulamak için kullandığım eski özyinelemeli fonksiyon tasarımını yeniyor.
decoder7283

7

Aşağıdaki kodla çok başarılı oldum (örnek: 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
Önereceğim tek değişiklik, 'func ("' + param" '")' yerine function () {func (param)} kullanmaktır. Bu şekilde, parametreyi bir dizeye dönüştürmeden ve geri döndürmeden doğrudan iletebilirsiniz. , bu çok kolay başarısız olabilir!
fabspro

@fabspro Bitti. Teşekkürler!
Nabil Kadimi

7
Bu sonsuz bir döngü değil mi? Sorunun bir retryLimit'i olduğu ve sunucunun asla geri gelmeyeceği için hizmet vermeyi istediği göz önüne alındığında ... Bunun gerçekten orada olması gerektiğini düşünüyorum
PandaWood

3
jQuery.ajaxSetup () Açıklama: Gelecekteki Ajax istekleri için varsayılan değerleri ayarlayın. Kullanımı tavsiye edilmez. api.jquery.com/jQuery.ajaxSetup
blub

2

Biri ararsa bu cevapların hiçbiri işe yaramaz .done() ajax çağrısından sonra çünkü gelecekteki geri aramaya eklemek için başarı yöntemine sahip olmayacaksınız. Yani biri bunu yaparsa:

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

Sonra mySuccessFunctekrar denemede aranmayacak. İşte benim çözümüm, buradaki @ cjpak'ın cevabından büyük ölçüde ödünç alınmış . Benim durumumda, AWS'nin API Gateway'i 502 hatasıyla yanıt verdiğinde yeniden denemek istiyorum.

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

Bu pasaj 2 saniye, ardından 5 saniye, ardından 10 saniye sonra geri çekilecek ve yeniden deneyecek ve bunu RETRY_WAIT sabitini değiştirerek düzenleyebilirsiniz.

AWS desteği, mavi ayda yalnızca bir kez gerçekleştiği için yeniden deneme eklememizi önerdi.


Bunu şimdiye kadarki tüm cevaplar arasında en yararlı buldum. Ancak, son satır TypeScript'te derlemeyi engeller. Bu işlevden herhangi bir şey döndürmeniz gerektiğini düşünmüyorum.
Freddie

0

İşte bunun için küçük bir eklenti:

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

Otomatik artan zaman aşımı buna iyi bir katkı olacaktır.

Global olarak kullanmak için $ .ajax imzasıyla kendi işlevinizi oluşturun, orada api'yi tekrar deneyin ve tüm $ .ajax çağrılarınızı yeni işlevinizle değiştirin.

Ayrıca doğrudan $ .ajax'ın yerine de geçebilirsiniz, ancak o zaman yeniden denemeden xhr çağrıları yapamazsınız.


0

Kitaplıkların eşzamansız yüklenmesi için benim için çalışan yöntem şu şekildedir:

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

Ardından .load_scriptuygulamanızdan arayın ve başarı geri aramalarınızı yerleştirin:

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

0

DemoUsers'ın cevabı Zepto ile çalışmıyor, çünkü bu hata fonksiyonunda Pencereyi gösteriyor. (Ve ajax'ı nasıl uyguladıklarını bilmediğiniz veya buna gerek olmadığı için 'bunu' kullanmanın bu yolu yeterince güvenli değildir.)

Zepto için, belki aşağıda deneyebilirsiniz, şimdiye kadar benim için iyi çalışıyor:

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

İsteğin evresel olduğundan emin olmak için yapıcıyı kullanın!


0

Kodunuz neredeyse dolu :)

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);
    }
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.