[javascript] jQuery를 사용하여 실패시 AJAX 요청을 재 시도하는 가장 좋은 방법은 무엇입니까?

의사 코드 :

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

어떤 종류의 지수 백 오프가 더 좋습니다.



답변

이 같은:


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


답변

한 가지 접근 방식은 래퍼 함수를 ​​사용하는 것입니다.

(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것입니다.


이 스 니펫을 그대로 콘솔에 복사하여 붙여 넣어 테스트 할 수 있습니다.


답변

아래 코드로 많은 성공을 거두었습니다 (예 : 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 );
        });
}


답변

.done()미래의 콜백에 연결할 성공 메서드가 없기 때문에 누군가가 ajax 호출 후에 호출하면 이러한 답변 중 어느 것도 작동 하지 않습니다. 그래서 누군가 이렇게하면 :

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

그런 다음 mySuccessFunc재 시도에서 호출되지 않습니다. 여기 @cjpak의 대답에서 크게 빌린 내 솔루션이 있습니다 . 제 경우에는 AWS의 API Gateway가 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 지원은 블루 문에서 한 번만 발생하므로 재 시도를 추가 할 것을 제안했습니다.


답변

다음은이를위한 작은 플러그인입니다.

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

자동 증가 시간 제한은 이에 대한 좋은 추가 기능입니다.

전역 적으로 사용하려면 $ .ajax 서명으로 고유 한 함수를 만들고 거기에서 api를 다시 시도하고 모든 $ .ajax 호출을 새 함수로 바꿉니다.

또한 $ .ajax를 직접 대체 할 수 있지만 다시 시도하지 않으면 xhr 호출을 할 수 없습니다.


답변

다음은 라이브러리의 비동기로드를 위해 저를 위해 일한 방법입니다.

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


답변

DemoUsers의 대답은 Zepto에서 작동하지 않습니다. 오류 기능의 이것이 Window를 가리 키기 때문입니다. (그리고 ‘this’를 사용하는 방법은 아약스를 구현하는 방법을 모르거나 그럴 필요가 없기 때문에 충분히 안전하지 않습니다.)

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

생성자를 사용하여 요청이 재진입되는지 확인하십시오!