JavaScript fetch with Timeout
The new
AbortController
and
AbortSignal
APIs have made canceling
fetch
requests much cleaner. To learn a more modern method of canceling a
fetch
request, please read
fetch with Timeout!
The fetch API started out as a target for criticism because of lack of timeout and request cancelation. While those criticisms could be argued as fair or not, you can't deny that the fetch API has been pretty awesome. As we've always done, if a feature is missing, we can always shim it in.
I've recently been thinking about shimming in a fetch timeout and found a good fetch / timeout script here. I've slightly modified it to prevent the fetch
call's then
and catch
callbacks from carrying out their tasks because I believe the timeout should be handled by the shim's Promise:
const FETCH_TIMEOUT = 5000;
let didTimeOut = false;
new Promise(function(resolve, reject) {
const timeout = setTimeout(function() {
didTimeOut = true;
reject(new Error('Request timed out'));
}, FETCH_TIMEOUT);
fetch('https://davidwalsh.name/?xx1')
.then(function(response) {
// Clear the timeout as cleanup
clearTimeout(timeout);
if(!didTimeOut) {
console.log('fetch good! ', response);
resolve(response);
}
})
.catch(function(err) {
console.log('fetch failed! ', err);
// Rejection already happened with setTimeout
if(didTimeOut) return;
// Reject with error
reject(err);
});
})
.then(function() {
// Request success and no timeout
console.log('good promise, no timeout! ');
})
.catch(function(err) {
// Error: response error, request timeout or runtime error
console.log('promise error! ', err);
});
Wrapping this code in a function called fetchWithTimeout
, whereby you pass in a timeout and fetch URL/settings would work well; since people like to use fetch in a variety of ways, I've chosen not to create a generalized function and instead am just providing the basic logic.
Many would argue that the timeout should come from the server but we all know us front-end devs don't always have control over both sides of a request. If you're looking for a fetch request timeout snippet, here you go!
![39 Shirts – Leaving Mozilla]()
In 2001 I had just graduated from a small town high school and headed off to a small town college. I found myself in the quaint computer lab where the substandard computers featured two browsers: Internet Explorer and Mozilla. It was this lab where I fell...
![How I Stopped WordPress Comment Spam]()
I love almost every part of being a tech blogger: learning, preaching, bantering, researching. The one part about blogging that I absolutely loathe: dealing with SPAM comments. For the past two years, my blog has registered 8,000+ SPAM comments per day. PER DAY. Bloating my database...
![Using Opacity to Show Focus with jQuery]()
A few days back I debuted a sweet article that made use of MooTools JavaScript and opacity to show focus on a specified element. Here's how to accomplish that feat using jQuery.
The jQuery JavaScript
There you have it. Opacity is a very simple but effective...
![Create a Dojo-Powered WordPress Website View]()
Yesterday I showed you WordPress' awesome JSON plugin named JSON API. Now that I can get my blog posts in JSON format, it's time to create an awesome AJAX'ed web app with that data. I've chosen to use the power of Dojo and Dijit to...
The core problem with fetch when it comes to cancelation or timeout is baked into the underlying interface: Promises. Stateful, eager Promises just don’t model cancelation very well because the concept of a chain-able future value and potential side-effects of calling for and having that future value resolve/error are too tightly coupled.
Newer apis seem to be adopting Promises as their model for async requests a bit too glibly, I think, not really understanding this structural problem. Modeling timeouts as errors might make sense in a lot of cases (though not all), but modeling cancelations as such is really problematic.
After the first time resolve() or reject() is called, subsequent calls to resolve() or reject() do absolutely nothing. Therefore, you don’t need to check whether the timeout has completed in order to keep from calling reject() or resolve().
Also, cancelling the timeout can be done more cleanly, using
Promise.prototype.finally
.With those two items in mind, here’s what the code would look like, wrapped in a function.