89

Вопрос по ajax, jquery, javascript – Как лучше всего повторить запрос AJAX при сбое с помощью jQuery?

Псевдокод:

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

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

  • Error: User Rate Limit ExceededtryCountError: User Rate Limit ExceededretryLimitError: User Rate Limit Exceededthis.retryLimit--; if (this.retryLimit) { ... $.ajax(this) ... }

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded.successError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceededapi.jquery.com/jQuery.ajaxSetup

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceededgithub.com/mberkom/jQuery.retryAjax

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Я не уверен, что это лучший способ, так что просто комментарий, но если вы вызываете свой ajax из функции, вы можете дать ему параметрtriesи при неудаче вы вызываете свою функцию сtries+1, Остановить исполнение наtries==3 или любой другой номер.

    от Nanne
  • Хорошее решение здесь:stackoverflow.com/questions/6298612/…

    от Jeremy A. West
  • возможный дубликатRetry a jquery ajax request which has callbacks attached to its deferred

    от user2284570
7 ответов
  • 7

    Нужно учесть

    One approach is to use a wrapper function:

    (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);
    
    Another approach would be to use a retries property on the $.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);
    }
    
    Another way (GIST) - override original $.ajax (better for 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') 
    });
    

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

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

  • 4

    Я имел большой успех с этим кодом ниже (пример:)

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

  • 203

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

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

  • 0

    Ни один из этих ответов не сработает, если кто-то позвонит

    .done() после их вызова ajax, потому что у вас не будет метода успешной привязки к будущему обратному вызову. Так что, если кто-то делает это:

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

    затемmySuccessFunc не будет вызван при повторной попытке. Вот мое решение, которое в значительной степени заимствовано из ответа @ cjpak.Вот, В моем случае я хочу повторить попытку, когда шлюз API AWS отвечает с ошибкой 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);
    });
    

    Этот фрагмент будет отключен и повторен через 2 секунды, затем 5 секунд, затем 10 секунд, которые вы можете редактировать, изменив константу RETRY_WAIT.

    Служба поддержки AWS предложила добавить повтор, поскольку это происходит для нас только один раз в голубой луне.

  • 0

    Вот метод

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

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

  • 0

    Ответ 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(){});
    

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

  • 0

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

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

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

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

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