Skip to content

Instantly share code, notes, and snippets.

@joshmcarthur
Created September 13, 2016 00:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshmcarthur/16f61bdd971fe1afa17c214578a5ec65 to your computer and use it in GitHub Desktop.
Save joshmcarthur/16f61bdd971fe1afa17c214578a5ec65 to your computer and use it in GitHub Desktop.
jQuery AJAX Prefilter for retrying failed XHR requests with backoff delay
/**
* This AJAX prefilter is based on http://stackoverflow.com/a/12840617 with some changes:
* 1. References to the question asker's context have been removed (they related to Auth)
* 2. Fixes have been made to code style at JSHint's request.
* 3. Assign max retries so that we can determine how many retries we STARTED from.
* 4. Add a setTimeout call so that the retried requests wait before performing the request.
* 5. Treat a '0' status as an error. (JM: I believe this is because Ember fakes it's own 'XHR' object
* that doesn't contain the actual request status code).
*/
/* globals jQuery */
jQuery.ajaxPrefilter(function (options, originalOptions, jqXHR) {
// Set up default options to use for retrying requests
var defaultOptions = {
maxRetries: 5, // Make 5 attempts at the API call before failing
delay: 1000, // ms to delay for between each call multiplied by remaining attempts (e.g. 1000, 2000, 3000, 4000, 5000)
};
// Don't infinitely recurse
originalOptions._retry = isNaN(originalOptions._retry) ? defaultOptions.maxRetries : originalOptions._retry - 1;
// Store _maxRetries, defaulting to the value of _retry
originalOptions._maxRetries = (originalOptions._maxRetries || originalOptions._retry);
// save the original error callback for later
if (originalOptions.error) {
originalOptions._error = originalOptions.error; }
// overwrite *current request* error callback
options.error = jQuery.noop();
// setup our own deferred object to also support promises that are only invoked
// once all of the retry attempts have been exhausted
var dfd = jQuery.Deferred();
jqXHR.done(dfd.resolve);
// if the request fails, do something else yet still resolve
jqXHR.fail(function () {
var args = Array.prototype.slice.call(arguments);
var retriesRemaining = originalOptions._retry;
var maxRetries = originalOptions._maxRetries;
var httpStatus = jqXHR.status;
if ((httpStatus === 0 || httpStatus >= 400) && retriesRemaining > 0) {
var delay = (maxRetries - retriesRemaining) * defaultOptions.delay;
console.warn("Request to " + jqXHR.url + " failed. Retrying " + retriesRemaining + " more times. Next retry in " + delay + " ms");
// retry with our modified deferred object after a delay.
setTimeout(function() {
jQuery.ajax(originalOptions).then(dfd.resolve, dfd.reject);
}, delay);
} else {
// add our _error callback to our promise object
if (originalOptions._error) { dfd.fail(originalOptions._error); }
dfd.rejectWith(jqXHR, args);
}
});
// NOW override the jqXHR's promise functions with our deferred
return dfd.promise(jqXHR);
});
@sudhakhayal
Copy link

I have this strange behaviour where two ajax requests get executed simultaneously. I simulate the test case by stopping the serve and then submitting the button that triggers the ajax request and then stop the server. Now it retries because of 503. I see separate requests being sent but my server logs show that two requests are executed simultaneously because of which duplicate records get created.
I assumed this was async so only when i get a callback will the next request go through. Can you help?
Below I have shown the flow of ajax requests and its console message:

(Original) ajaxReq1 Method: GET; status : (cancelled/ 0);
Request to url with status 0 failed. Retrying 3 more times. Next retry in 0 ms

(Retry 1) ajaxReq1 Method: GET; status : (cancelled/ 0);
Request to url with status 0 failed. Retrying 2 more times. Next retry in 5000 ms

(Retry 2) ajaxReq1 Method: GET; status : (cancelled/ 0);
Request to url with status 0 failed. Retrying 1 more times. Next retry in 10000 ms

(Retry 3) ajaxReq1 Method: GET; status : (200);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment