Created
September 13, 2016 00:14
-
-
Save joshmcarthur/16f61bdd971fe1afa17c214578a5ec65 to your computer and use it in GitHub Desktop.
jQuery AJAX Prefilter for retrying failed XHR requests with backoff delay
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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);