使用 jQuery 在失败时重试 AJAX 请求的最佳方法是什么?

IT技术 javascript jquery ajax xmlhttprequest
2021-01-24 21:32:50

伪代码:

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

更好的是某种指数退避

6个回答

像这样的东西:


$.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
        }
    }
});
@MichaelBerkompas - 你的插件还能用吗?它已经 2 年没有收到提交了。
2021-03-18 21:32:50
tryCountretryLimit过分。考虑仅使用 1 个变量:this.retryLimit--; if (this.retryLimit) { ... $.ajax(this) ... }
2021-03-24 21:32:50
我采用了@Sudhir 的解决方案并在 github 上创建了一个 $.retryAjax 插件:github.com/mberkom/jQuery.retryAjax
2021-04-01 21:32:50
这对我不起作用。条件中的 this.tryCount 始终为 1。
2021-04-03 21:32:50
如果另一个回调处理程序.success附加到调用返回此 ajax 请求的函数,这会起作用吗?
2021-04-09 21:32:50

一种方法是使用包装函数:

(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方法之前没有被包装,以避免相同的代码运行两次。


您可以将这些片段(按原样)复制粘贴到控制台以进行测试

感谢您教我如何构建包装器!这击败了我用来实现的旧递归函数设计。
2021-03-24 21:32:50
@SevbanÖztürk - 你是什么意思?你试一试 :)
2021-03-25 21:32:50
谢谢你的剧本。它适用于 $.ajaxSetup 吗?
2021-04-08 21:32:50

我在下面的代码中取得了很多成功(例如: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 );
        });
}
这不是无限循环吗?鉴于问题有一个 retryLimit 并且显然是想迎合永远不会回来的服务器......我认为这真的必须在那里
2021-03-13 21:32:50
@fabspro 完成。谢谢!
2021-03-22 21:32:50
jQuery.ajaxSetup() 描述:为未来的 Ajax 请求设置默认值。不推荐使用它。api.jquery.com/jQuery.ajaxSetup
2021-03-22 21:32:50
我建议的唯一更改是用 function(){func(param)} 替换 'func("'+param"'")'。这样,您可以直接传递参数,而无需将其转换为字符串并返回,这很容易失败!
2021-03-30 21:32:50

如果有人.done()在他们的 ajax调用之后调用,这些答案都不起作用,因为您将没有成功方法来附加到未来的回调。所以如果有人这样做:

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

然后mySuccessFunc在重试时不会被调用。这里是我的解决方案,这在很大程度上来自@ cjpak的答案借这里就我而言,我想在 AWS 的 API 网关响应 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 中进行编译。我认为你不应该从这个函数返回任何东西。
2021-04-06 21:32:50

这是一个小插件:

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

自动递增超时将是一个很好的补充。

要在全局范围内使用它,只需使用 $.ajax 签名创建您自己的函数,在那里使用 retry api 并用您的新函数替换所有 $.ajax 调用。

您也可以直接替换 $.ajax,但是如果不重试,您将无法进行 xhr 调用。