4

I need to create a function that retries a failed ajax request until a given limit, but I need to ensure the promise is only rejected when the maximum of retries was exceeded, like this:

function my_ajax(...) { ... }

// then I use it this way
return $.when(my_ajax('foo.json'), my_ajax('bar.json'))

In order to work with jQuery.when, only one promise should be returned of my_ajax, which should be resolved when the internal jQuery.ajax is resolved, and rejected only when the maximum of retries was made.

The code I made is this:

function do_ajax(args, dfd, attempt) {
    dfd     || (dfd = $.Deferred());
    attempt || (attempt = 1);

    $.ajax(args).then(dfd.resolve, function(xhr, text_status, error_thrown) {
        console.error(/* something useful */);
        attempt++;

        if(attempt > 3) {
            dfd.reject(xhr, text_status, error_thrown);
        } else {
            do_ajax(args, dfd, attempt);
        }
    });

    return dfd.promise();
}

// in some random code
return $.when(do_ajax({url:'foo.json'}), do_ajax({url:'bar.json'});

This works for me*, but it's kind of hard to understand. The question is: is there a better (and easier to read) way to do it?

* - Actually I didn't tested to fail sometimes, but I works fine when the first ajax request is successful.

Lucas Sampaio
  • 1,568
  • 2
  • 18
  • 36
  • possible duplicate of [What's the best way to retry an AJAX request on failure using jQuery?](http://stackoverflow.com/questions/10024469/whats-the-best-way-to-retry-an-ajax-request-on-failure-using-jquery) – user2284570 Oct 10 '14 at 13:57

2 Answers2

4

I think your code is the right way to do it, wrap your call in a new Deferred and return it until all attempts are reached.

You could try to include your recursive call inside a anonymous function to improve readibility maybe.

I wrote it like that :

function doAjax(ajaxArgs, attempt) {
    // the wrapped returned dfd
    var wrappedDfd = $.Deferred();

    // your nested call using attempt counter
    (function nestedCall() {
        // if call succed, resolve the wrapped dfd
        $.ajax(ajaxArgs).then(wrappedDfd.resolve, function() {
            // update attempt counter
            attempt--;
            if (attempt > 0) {
                // try another call
                nestedCall();
            } else {
                // max try reached, reject wrapped dfd
                wrappedDfd.reject();
            }
        });
    })();

    return wrappedDfd.promise();
};

which is really similar to your code.

Here is the jsfiddle. For test purpose, I replaced doAjax arguments by a mock factory call, but the code stay the same.

Hope this help.

Mordhak
  • 2,646
  • 2
  • 20
  • 14
2

Based on Mordhak's answer and a few others around SO dherman has created a jQuery plugin: jquery.ajaxRetry (GitHub repo) and so has johnkpaul : jquery.ajax-retry

jquery.ajaxRetry

Example

$.ajax({
  // Retry this request up to 2 times
  shouldRetry: 2
});
  • Can retry indefinitely
  • Can retry n times
  • Can run a function and evaluate to true or false in shouldRetry to determine if it should keep retrying

jquery.ajax-retry

Example

$.ajax(options).retry({times:3, timeout:3000}).then(function(){
    alert("success!");
  }); 
  • Can retry n times
  • Can specify amount of time to wait between retries
  • Can specify what status codes to retry for
Community
  • 1
  • 1
bbodenmiller
  • 3,101
  • 5
  • 34
  • 50