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


107

의사 코드 :

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

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


1
이것이 최선의 방법인지는 잘 모르겠습니다. 그래서 그냥 주석을 달았지만, 기능에서 ajax를 호출하면 매개 변수를 줄 수 있고 tries실패하면 tries+1. tries==3또는 다른 번호 에서 실행을 중지합니다 .
Nanne


답변:


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
나는 수 디르의 솔루션 @ 촬영과 $ .retryAjax 플러그인에 github에 여기 만들었습니다 github.com/mberkom/jQuery.retryAjax
마이클 Berkompas

2
이것은 나를 위해 작동하지 않습니다. 조건부의 this.tryCount는 항상 1입니다.
user304602 2013

2
@MichaelBerkompas-플러그인이 여전히 작동합니까? 2 년 동안 커밋을받지 못했습니다.
Hendrik

2
.success이 ajax 요청을 반환하는 함수를 호출하는 데 다음 과 같은 다른 콜백 핸들러 가 첨부되어 있으면 작동 합니까?
ProblemsOfSumit

17
의 쌍 tryCountretryLimit과도한이다. 단 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과 함께 작동합니까?
Sevban Ö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" ' ")'을 function () {func (param)}으로 바꾸는 것입니다. 이렇게하면 매개 변수를 문자열로 변환하지 않고 직접 전달할 수 있습니다. , 매우 쉽게 실패 할 수 있습니다!
fabspro 2013-09-28

@fabspro 완료. 감사!
Nabil Kadimi

7
이것은 끝없는 루프가 아닙니까? 질문에 retryLimit이 있고 서버가 다시 돌아 오지 않도록 확실히하고 싶은 것이 있습니다 ... 저는 이것이 정말로 거기에 있어야한다고 생각합니다
PandaWood

3
jQuery.ajaxSetup () 설명 : 향후 Ajax 요청에 대한 기본값을 설정합니다. 사용하지 않는 것이 좋습니다. api.jquery.com/jQuery.ajaxSetup
blub

2

.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 지원은 블루 문에서 한 번만 발생하므로 재 시도를 추가 할 것을 제안했습니다.


나는 이것이 지금까지 모든 답변 중에서 가장 유용하다는 것을 알았습니다. 그러나 마지막 줄은 TypeScript에서 컴파일하는 것을 방지합니다. 이 함수에서 아무것도 반환해서는 안된다고 생각합니다.
Freddie

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를 가리 키기 때문입니다. (그리고 '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(){});

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


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);
    }
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.