(This was originally posted at my development blog on chrisarmstrong.dev. Check it out for more AWS and DynamoDB related articles!)
Although none of these are built-in, it's easy to add retries, timeouts and cancellations to your fetch() calls without needing to use a full-featured third-party library.
Retries
By wrapping your fetch handler in a recursive function that returns a promise, you can easily get retry behaviour:
const fetchWithRetries = async (
url, options, retryCount = 0) => {
// split out the maxRetries option from the remaining options
// (with a default of 3 retries)
const { maxRetries = 3, ...remainingOptions } = options;
try {
return await fetch(url, remainingOptions);
} catch (error) {
// if the retryCount has not been exceeded, call again with retryCount + 1
if (retryCount < maxRetries) {
return fetchWithRetries(url, options, retryCount + 1)
}
// max retries exceeded, just throw error
throw error;
}
}
You could even customise it further with additional flags to control when to retry, or to throw a MaxRetriesError
when the maxRetries
count is hit.
Timeout
This one is fairly easy - we use setTimeout
to reject the promise when there is a timeout error.
// Create a promise that rejects after `timeout` milliseconds
const throwOnTimeout = (timeout) =>
new Promise((_, reject) =>
setTimeout(() =>
reject(new Error("Timeout")),
timeout
),
);
const fetchWithTimeout = (url, options = {}) => {
const { timeout, ...remainingOptions } = options;
// if the timeout option is specified, race the fetch call
if (timeout) {
return Promise.race(fetch(url, remainingOptions), throwOnTimeout(timeout));
}
return fetch(url, remainingOptions);
}
We use Promise.race to run both the fetch()
call and the setTimeout()
call at the same time - whichever resolves or rejects first will resolve or reject the Promise
(respectively), with the other result being ignored.
Cancel
This one is documented on MDN, but for completeness here it is below.
const fetchWithCancel = (url, options = {}) => {
const controller = new AbortController();
const call = fetch(url, { ...options, signal: controller.signal });
const cancel = () => controller.abort();
return [call, cancel ];
};
In the above, we return a tuple with the returned promise and a cancel
function that allows the user to cancel the request if needed.
An example showing its usage is below:
// We don't await this call, just capture the promise
const [promise, cancel] = fetchWithCancel('https://cataas.com/cat?json=true');
// await the promise to get the response
const response = await promise;
// ...
// cancel the request (e.g. if we have rendered something else)
cancel();
Top comments (0)