Какой лучший способ повторить запрос AJAX при сбое с помощью jQuery?

Псевдокод:

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

Еще лучше будет какой-то экспоненциальный откат

Ответ 1

Что-то вроде этого:


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

Ответ 2

У меня был большой успех с этим кодом ниже (пример: 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 );
        });
}

Ответ 3

Один из подходов заключается в использовании функции-обертки:

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

Другой способ (GIST) - переопределить оригинал $.ajax (лучше для 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') 
});

То, что нужно рассмотреть, заключается в том, что уверен метод $.ajax ранее не был завернут, чтобы избежать того же кода, который выполняется дважды.


Вы можете скопировать эти фрагменты (как есть) в консоль, чтобы протестировать их

Ответ 4

Вот небольшой плагин для этого:

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

Автоматическое увеличение времени ожидания будет хорошим дополнением к нему.

Чтобы использовать его во всем мире, просто создайте свою собственную функцию с сигнатурой $.ajax, используйте там apry retry и замените все ваши вызовы $.ajax своей новой функцией.

Также вы можете напрямую заменить $.ajax, но после этого вы не сможете совершать xhr-вызовы без повтора.

Ответ 5

Здесь метод, который работал у меня для асинхронной загрузки библиотек:

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

Затем просто вызовите .load_script из своего приложения и вставьте свой обратный вызов успеха:

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

Ответ 6

Ответ DemoUsers не работает с Zepto, так как это в функции ошибки указывает на Window. (И этот способ использования 'this' недостаточно безопасен, так как вы не знаете, как они реализуют ajax или не нуждаются.)

Для Zepto, может быть, вы могли бы попробовать ниже, до сих пор это хорошо работает для меня:

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

Используйте конструктор, чтобы убедиться, что запрос реентерабелен!