Apa cara terbaik untuk mencoba kembali permintaan AJAX jika gagal menggunakan jQuery?

107

Kode semu:

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

Yang lebih baik lagi adalah semacam kemunduran eksponensial

Tom Lehman
sumber
1
Saya tidak yakin apakah ini cara terbaik, jadi hanya komentar, tetapi jika Anda memanggil ajax dari fucntion, Anda dapat memberikannya parameter tries, dan jika gagal, Anda memanggil fungsi dengan tries+1. Hentikan eksekusi pada tries==3atau nomor lain.
Nanne
Solusi bagus di sini: stackoverflow.com/questions/6298612/…
Jeremy A. West

Jawaban:

238

Sesuatu seperti ini:


$.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
        }
    }
});
Sudhir Bastakoti
sumber
12
Saya telah mengambil solusi @ Sudhir dan membuat plugin $ .retryAjax di github di sini: github.com/mberkom/jQuery.retryAjax
Michael Berkompas
2
Ini tidak berhasil untuk saya. this.tryCount dalam kondisional selalu 1.
user304602
2
@MichaelBerkompas - apakah plugin Anda masih berfungsi? Itu belum menerima komit dalam 2 tahun.
Hendrik
2
Akankah ini berfungsi jika penangan panggilan balik lain seperti .successdilampirkan untuk memanggil fungsi yang mengembalikan permintaan ajax ini?
ProblemsOfSumit
17
Sepasang tryCountdan retryLimitberlebihan. Pertimbangkan untuk menggunakan hanya 1 variabel:this.retryLimit--; if (this.retryLimit) { ... $.ajax(this) ... }
vladkras
15

Salah satu pendekatannya adalah dengan menggunakan fungsi pembungkus:

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

Pendekatan lain akan menggunakan retriesproperti di$.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);
}

Cara lain ( GIST ) - timpa yang asli $.ajax(lebih baik untuk KERING)

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

Hal yang perlu dipertimbangkan adalah memastikan bahwa $.ajaxmetode tersebut belum digabungkan sebelumnya, untuk menghindari kode yang sama berjalan dua kali.


Anda dapat menyalin-tempel cuplikan ini (sebagaimana adanya) ke konsol untuk mengujinya

vsync
sumber
Terima kasih untuk naskahnya. Apakah ini berfungsi dengan $ .ajaxSetup?
Sevban Öztürk
@ SevbanÖztürk - apa maksud Anda? coba saja :)
vsync
Terima kasih telah mengajari saya cara menyusun pembungkus! Ini mengalahkan desain fungsi rekursif lama yang biasa saya terapkan.
decoder7283
7

Saya telah banyak sukses dengan kode di bawah ini (contoh: 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 );
        });
}
Nabil Kadimi
sumber
4
Satu-satunya perubahan yang saya sarankan adalah mengganti 'func ("' + param" '")' dengan function () {func (param)}. Dengan begitu, Anda bisa langsung meneruskan parameter tanpa mengubahnya menjadi string dan kembali , yang bisa gagal dengan sangat mudah!
fabspro
@abdully Terima kasih!
Nabil Kadimi
7
Bukankah ini putaran tanpa akhir? Mengingat pertanyaan memiliki retryLimit dan jelas ingin melayani server yang tidak pernah kembali ... Saya pikir ini benar-benar harus ada di sana
PandaWood
3
jQuery.ajaxSetup () Deskripsi: Setel nilai default untuk permintaan Ajax di masa mendatang. Penggunaannya tidak disarankan. api.jquery.com/jQuery.ajaxSetup
blub
2

Tak satu pun dari jawaban ini berfungsi jika seseorang menelepon .done()setelah panggilan ajax mereka karena Anda tidak akan memiliki metode yang berhasil untuk dilampirkan ke panggilan balik di masa mendatang. Jadi jika seseorang melakukan ini:

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

Maka mySuccessFunctidak akan dipanggil saat coba lagi. Inilah solusi saya, yang banyak dipinjam dari jawaban @ cjpak di sini . Dalam kasus saya, saya ingin mencoba lagi ketika AWS's API Gateway merespons dengan kesalahan 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);
});

Cuplikan ini akan mundur dan mencoba lagi setelah 2 detik, lalu 5 detik, lalu 10 detik, yang dapat Anda edit dengan mengubah konstanta RETRY_WAIT.

Dukungan AWS menyarankan agar kami menambahkan percobaan ulang, karena ini hanya terjadi sekali di bulan biru.

Ryan Shillington
sumber
Saya menemukan ini menjadi yang paling berguna dari semua jawaban sejauh ini. Namun, baris terakhir mencegah kompilasi di TypeScript. Saya tidak berpikir Anda harus mengembalikan apa pun dari fungsi ini.
Freddie
0

Berikut adalah plugin kecil untuk ini:

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

Batas waktu penambahan otomatis akan menjadi tambahan yang bagus untuk itu.

Untuk menggunakannya secara global cukup buat fungsi Anda sendiri dengan tanda tangan $ .ajax, gunakan di sana coba lagi api dan ganti semua panggilan $ .ajax Anda dengan fungsi baru Anda.

Anda juga dapat langsung mengganti $ .ajax, tetapi Anda tidak akan dapat melakukan panggilan xhr tanpa mencoba lagi.

Oleg Isonen
sumber
0

Inilah metode yang berfungsi untuk saya untuk pemuatan pustaka asinkron:

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

Kemudian cukup panggil .load_scriptdari aplikasi Anda dan tingkatkan panggilan balik sukses Anda:

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

Jawaban DemoUsers tidak berfungsi dengan Zepto, karena ini dalam fungsi kesalahan mengarah ke Window. (Dan cara menggunakan 'ini' tidak cukup aman karena Anda tidak tahu bagaimana mereka mengimplementasikan ajax atau tidak perlu.)

Untuk Zepto, mungkin Anda bisa mencoba di bawah ini, sampai sekarang ini berfungsi dengan baik untuk saya:

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

Gunakan konstruktor untuk memastikan permintaan reentrant!

Xhua
sumber
0

Kode Anda hampir penuh :)

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