Який найкращий спосіб повторити повторний запит AJAX при відмові за допомогою jQuery?


107

Псевдокод:

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

Ще краще було б якесь експоненціальне відступлення


1
Я не впевнений, що це найкращий спосіб взагалі, тому просто коментар, але якщо ви зателефонуєте своєму аяксу з fucntion, ви можете надати йому параметр tries, а в разі невдачі ви подзвоните свою функцію tries+1. Зупинити виконання на tries==3або будь-якому іншому номері.
Нанна


Відповіді:


238

Щось на зразок цього:


$.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 і створив плагін $ .retryAjax на github тут: github.com/mberkom/jQuery.retryAjax
Michael Berkompas

2
Це не працює для мене. this.tryCount у умовному завжди 1.
user304602

2
@MichaelBerkompas - ваш плагін все ще працює? Вона не отримала комісій за 2 роки.
Гендрик

2
чи буде це працювати, якщо інший обробник зворотного виклику, як-от .successприєднаний до виклику функції, яка повертає цей запит ajax?
ПроблемиFoSumit

17
Пара tryCountі retryLimitнадмірна. Подумайте про використання лише 1 змінної:this.retryLimit--; if (this.retryLimit) { ... $.ajax(this) ... }
vladkras

15

Один із підходів полягає у використанні функції обгортки:

(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метод не був завершений раніше, щоб уникнути того самого коду, який працює два рази.


Ви можете скопіювати і вставити ці фрагменти (як є) на консоль, щоб перевірити їх


Дякую за сценарій Це працює з $ .ajaxSetup?
Севбан Öztürk

@ SevbanÖztürk - що ти маєш на увазі? просто спробуйте :)
vsync

Дякую, що ви навчили мене структурувати обгортку! Це перевершує стару рекурсивну функцію, яку я використовував для реалізації.
декодер7283

7

Я мав багато успіху з цим кодом нижче (приклад: 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
Єдина пропозиція, яку я б запропонував, - це замінити 'func ("' + param" '")' функцією () {func (param)}. Таким чином, ви можете безпосередньо передати параметр, не перетворюючи його в рядок і назад , який може вийти з ладу дуже легко!
fabspro

@fabspro Готово. Дякую!
Набіл Кадімі

7
Це не безкінечна петля? З огляду на те, що питання має повторний ліміт і, очевидно, хоче, щоб сервер не повертався ... Я думаю, що це справді має бути там
PandaWood

3
jQuery.ajaxSetup () Опис: Встановіть значення за замовчуванням для майбутніх запитів Ajax. Його використання не рекомендується. api.jquery.com/jQuery.ajaxSetup
blub

2

Жоден із цих відповідей не спрацьовує, якщо хтось дзвонить .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 запропонувала нам додати повторного спроби, оскільки це трапляється для нас лише раз у синій місяць.


Я вважав це найбільш корисним з усіх відповідей поки що. Однак останній рядок перешкоджає компіляції в TypeScript. Я не думаю, що вам слід нічого не повертати з цієї функції.
Фредді

0

Ось невеликий плагін для цього:

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

Авто час збільшення тайм-ауту було б хорошим доповненням до нього.

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

Також ви можете безпосередньо замінити $ .ajax, але ви не зможете робити дзвінки xhr без повторного спроби.


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. (І спосіб використання "цього" недостатньо безпечний, оскільки ви не знаєте, як вони реалізують 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

Ваш код майже повний :)

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);
    }
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.