Cách tốt nhất để thử lại yêu cầu AJAX khi không sử dụng jQuery là gì?


107

Mã giả:

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

Thậm chí tốt hơn sẽ là một số loại dự phòng theo cấp số nhân


1
Tôi không chắc liệu đây có phải là cách tốt nhất hay không, vì vậy hãy chỉ nhận xét, nhưng nếu bạn gọi ajax của mình từ một fucntion, bạn có thể cung cấp cho nó một tham số triesvà nếu thất bại thì bạn gọi hàm của mình bằng tries+1. Ngừng thực hiện trên tries==3hoặc bất kỳ số nào khác.
Nanne


Thoải mái giải pháp ở đây: stackoverflow.com/questions/6298612/...
Jeremy A. Tây

Câu trả lời:


238

Một cái gì đó như thế này:


$.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
Tôi đã sử dụng giải pháp của @ Sudhir và tạo một plugin $ .retryAjax trên github tại đây: github.com/mberkom/jQuery.retryAjax
Michael Berkompas 17/09/12

2
Điều này không làm việc cho tôi. this.tryCount trong điều kiện luôn luôn là 1.
user304602

2
@MichaelBerkompas - plugin của bạn vẫn hoạt động chứ? Nó đã không nhận được cam kết trong 2 năm.
Hendrik

2
điều này sẽ hoạt động nếu một trình xử lý gọi lại khác như .successđược đính kèm để gọi hàm trả về yêu cầu ajax này?
ProblemsOfSumit

17
Cặp tryCountretryLimitlà quá mức. Xem xét sử dụng chỉ có 1 biến:this.retryLimit--; if (this.retryLimit) { ... $.ajax(this) ... }
vladkras

15

Một cách tiếp cận là sử dụng một hàm wrapper:

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

Một cách tiếp cận khác sẽ là sử dụng một thuộc retriestính trên$.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);
}

Một cách khác ( GIST ) - ghi đè bản gốc $.ajax(tốt hơn cho 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') 
});

Một điểm cần xem xét là làm cho chắc chắn các $.ajaxphương pháp đã được chưa bọc trước đây, để tránh cùng mã chạy hai lần.


Bạn có thể sao chép và dán các đoạn mã này (nguyên trạng) vào bảng điều khiển để kiểm tra chúng


Cảm ơn vì kịch bản. Nó có hoạt động với $ .ajaxSetup không?
Sevban Öztürk

@ SevbanÖztürk - ý bạn là gì? chỉ cần thử :)
vsync

Cảm ơn vì đã dạy tôi cách cấu trúc một trình bao bọc! Điều này đánh bại thiết kế hàm đệ quy cũ mà tôi đã từng thực hiện.
decoder7283,

7

Tôi đã rất thành công với đoạn mã này bên dưới (ví dụ: 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
Thay đổi duy nhất tôi muốn đề xuất là thay thế 'func ("' + param" '")' bằng function () {func (param)}. Bằng cách đó, bạn có thể truyền trực tiếp tham số mà không cần chuyển đổi nó thành một chuỗi và quay lại , có thể thất bại rất dễ dàng!
fabspro

@fabspro Đã xong. Cảm ơn!
Nabil Kadimi

7
Đây không phải là một vòng lặp vô tận sao? Với câu hỏi có retryLimit và rõ ràng là muốn phục vụ cho máy chủ không bao giờ quay trở lại ... Tôi nghĩ điều này thực sự phải có
PandaWood

3
jQuery.ajaxSetup () Mô tả: Đặt giá trị mặc định cho các yêu cầu Ajax trong tương lai. Việc sử dụng nó không được khuyến khích. api.jquery.com/jQuery.ajaxSetup
blub

2

Không có câu trả lời nào trong số này hoạt động nếu ai đó gọi .done()sau cuộc gọi ajax của họ vì bạn sẽ không có phương pháp thành công để đính kèm vào lần gọi lại trong tương lai. Vì vậy, nếu ai đó làm điều này:

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

Sau đó, mySuccessFuncsẽ không được gọi khi thử lại. Đây là giải pháp của tôi, được vay mượn nhiều từ câu trả lời của @ cjpak ở đây . Trong trường hợp của tôi, tôi muốn thử lại khi API Gateway của AWS phản hồi với lỗi 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);
});

Đoạn mã này sẽ quay lại và thử lại sau 2 giây, sau đó 5 giây, rồi 10 giây, bạn có thể chỉnh sửa đoạn mã này bằng cách sửa đổi hằng số RETRY_WAIT.

Bộ phận hỗ trợ AWS đã đề xuất chúng tôi thêm một lần thử lại, vì nó chỉ xảy ra với chúng tôi một lần trong tuần trăng xanh.


Tôi thấy đây là câu trả lời hữu ích nhất trong số tất cả các câu trả lời cho đến nay. Tuy nhiên, dòng cuối cùng ngăn quá trình biên dịch trong TypeScript. Tôi không nghĩ rằng bạn nên trả lại bất cứ thứ gì từ chức năng này.
Freddie

0

Đây là một plugin nhỏ cho việc này:

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

Tự động tăng thời gian chờ sẽ là một bổ sung tốt cho nó.

Để sử dụng nó trên toàn cầu, chỉ cần tạo hàm của riêng bạn với chữ ký $ .ajax, sử dụng ở đó thử lại api và thay thế tất cả các lệnh gọi $ .ajax bằng hàm mới của bạn.

Ngoài ra, bạn có thể thay thế trực tiếp $ .ajax, nhưng bạn sẽ không thể thực hiện cuộc gọi xhr mà không thử lại sau đó.


0

Đây là phương pháp phù hợp với tôi để tải thư viện không đồng bộ:

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

Sau đó, chỉ cần gọi .load_scripttừ ứng dụng của bạn và lồng cuộc gọi lại thành công của bạn:

$.loadScript('maps', '//maps.google.com/maps/api/js?v=3.23&libraries=geometry&libraries=places&language=&hl=&region=', function(){
    initialize_map();
    loadListeners();
});

0

Câu trả lời của DemoUsers không hoạt động với Zepto, vì điều này trong hàm lỗi đang trỏ đến Window. (Và cách sử dụng 'this' không đủ an toàn vì bạn không biết cách họ triển khai ajax hoặc không cần thiết.)

Đối với Zepto, có thể bạn có thể thử bên dưới, cho đến bây giờ nó hoạt động tốt đối với tôi:

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

Sử dụng hàm tạo để đảm bảo yêu cầu được đưa vào lại!


0

Mã của bạn gần đầy :)

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);
    }
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.