your programing

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

lovepro 2023. 4. 2. 12:14
반응형

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(건조 시 더 좋습니다)

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

코드가 거의 꽉 찼습니다:)

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

누군가 전화를 걸면 이 대답들 중 어느 것도 효과가 없다..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초 후에 재시도합니다.이 스니펫은 RETTRI_WAIT 상수를 수정하여 편집할 수 있습니다.

AWS 지원에서는 재시도를 추가할 것을 제안했습니다.그것은 극히 드문 일이기 때문입니다.

이를 위한 작은 플러그인은 다음과 같습니다.

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

자동 증분 타임아웃을 추가하는 것이 좋습니다.

글로벌하게 사용하려면 $.ajax 시그니처를 사용하여 자체 함수를 만들고 그곳에서 retry 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();
});

Demo Users의 답변은 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(){});

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

@vsync 3rd 코드로 특정 문제를 해결했습니다.

$.ajax = (($oldAjax) => {
    
  var df = $.Deferred();
  
  // on fail, retry by creating a new Ajax deferred
  function check(self, status) {
    console.log("check " + status + " => " + self.retries);
    const shouldRetry = status != 'success' && status != 'parsererror';
    if (shouldRetry && self.retries > 0) {
      setTimeout(() => {
        console.log("retry " + self.retries);
        $.ajax(self);
      }, self.retryInterval || 100);
    }
  }

  function failed(jqXHR, status, e) {
    if (this.retries - 1 <= 0) {
      // 재시도 횟수가 끝나면, 오류 보내기
      df.reject(KfError.convertKfError(jqXHR, this.url));
    } else {
      this.retries --;
      check(this, 'retry', this.retries);
    }
  }

  function done(res, textStatus, jqXHR) {
    if (!res.success) { // 200 코드이지만, 응답에 실패라면 오류로 처리
      if (this.retries - 1 <= 0) {
        df.reject(KfError.createResponseError(res, this.url));
      } else {
        this.retries --;
        check(this, 'retry', this.retries)
      }
    } else {
      df.resolve(res, textStatus, jqXHR);
    }
  }
  return function (settings) {
    $oldAjax(settings)
      .fail(failed)
      .done(done);
    return df;
  };
})($.ajax);

function createRequest(url) {
  return $.ajax({
    type: 'GET',
    url: url,
    timeout: 2000,
    retries: 3,
    retryInterval: 1000
  });
}

$(function () {
  createRequest(Rest.correctUrl('/auth/refres'))
    .then((res) => {
      console.log('ok res');
    })
    .catch((e) => {
      // Finally catch error after retrial.
      console.log(e);
    });
});

언급URL : https://stackoverflow.com/questions/10024469/whats-the-best-way-to-retry-an-ajax-request-on-failure-using-jquery

반응형