Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

People also ask

What happens if an AJAX request made using jQuery fails?

Whenever an Ajax request completes with an error, jQuery triggers the ajaxError event. Any and all handlers that have been registered with the . ajaxError() method are executed at this time. Note: This handler is not called for cross-domain script and cross-domain JSONP requests.

How do you handle AJAX failure?

The best way to bubble that error from the server side (using php) to the client side is to send a header through the Ajax request somewhere in the 400's (which is always associated with errors). Once the Ajax request receives this it will trigger your error function.

What is the correct approach to handle AJAX error when AJAX request fails?

To handle jQuery AJAX error. The ajaxError( callback ) method attaches a function to be executed whenever an AJAX request fails.

How check AJAX request is successful jQuery?

$. ajax({ url: "page. php", data: stuff, success: function(response){ console. log("success"); } });


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

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 retries property on the $.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)

// 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 sure the $.ajax method wasn't already wrapped previously, in order to avoid the same code running twice.


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


I've had a lot of success with this code below (example: 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 );
        });
}

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:

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

Then mySuccessFunc won'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.

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.

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