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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10024469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 23:32:35  来源:igfitidea点击:

What's the best way to retry an AJAX request on failure using jQuery?

javascriptjqueryajaxxmlhttprequest

提问by Tom Lehman

Pseudo code:

伪代码:

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

Even better would be some kind of exponential back-off

更好的是某种指数退避

回答by Sudhir Bastakoti

Something like this:

像这样的东西:


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

回答by vsync

One approach is to use a wrapper function:

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

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

Another approach would be to use a retriesproperty on the $.ajax

另一种方法是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);
}

Another way (GIST) - override original $.ajax(better for DRY)

另一种方式(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') 
});

A point to consider is making surethe $.ajaxmethod wasn't already wrapped previously, in order to avoid the same code running twice.

需要考虑的一点是确保$.ajax方法之前没有被包装,以避免相同的代码运行两次。



You can copy-paste these snippets (as-is) to the console to test them

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

回答by Nabil Kadimi

I've had a lot of success with this code below (example: http://jsfiddle.net/uZSFK/)

我在下面的代码中取得了很多成功(例如: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 );
        });
}

回答by Ryan Shillington

None of these answers work if somebody calls .done()after their ajax call because you won't have the success method to attach to the future call back. So if somebody does this:

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

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

Then mySuccessFuncwon't get called on the retry. Here's my solution, which is heavily borrowed from @cjpak's answer here. In my case I want to retry when AWS's API Gateway responds with 502 error.

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

This snippet will back-off and retry after 2 seconds, then 5 seconds, then 10 seconds, which you can edit by modifying the RETRY_WAIT constant.

此代码段将在 2 秒、5 秒和 10 秒后退避并重试,您可以通过修改 RETRY_WAIT 常量进行编辑。

AWS support suggested we add a retry, since it happens for us only once in a blue moon.

AWS 支持建议我们添加重试,因为它只会在蓝月亮中发生一次。

回答by Andriy

Your code is almost full :)

您的代码几乎已满:)

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

回答by Oleg Isonen

Here is a small plugin for this:

这是一个小插件:

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

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

Auto incrementing timeout would be a good addition to it.

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

To use it globally just create your own function with $.ajax signature, use there retry api and replace all your $.ajax calls by your new function.

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

Also you could directly replace $.ajax, but you will not be able to make xhr calls without retry then.

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

回答by Abram

Here's the method that worked for me for asynchronous loading of libraries:

这是对我有用的异步加载库的方法:

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

Then just call .load_scriptfrom your app and nest your success callback:

然后只需.load_script从您的应用程序调用并嵌套您的成功回调:

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

回答by Xhua

DemoUsers's answer doesn't work with Zepto, since this in the error function is pointing to Window. (And that way of using 'this' is not secure enough as you don't know how they implement ajax or no need to.)

DemoUsers 的答案不适用于 Zepto,因为错误函数中的 this 指向 Window。(这种使用 'this' 的方式不够安全,因为您不知道它们如何实现 ajax 或不需要。)

For Zepto, maybe you could try below, till now it works well for me:

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

Use constructor to make sure request is reentrant!

使用构造函数来确保请求是可重入的!