Adding Timeout to Fetch API
By twahyono
The goal
We want to add timeout capability to already excellent Fetch API with something as simple as adding timeout parameter:
var resp = await fetch(someUrl);
to something like:
var resp = await fetch(someUrl,timeout);
Introducing AbortController
It allows you to abort one or more web request when desired by sending abort signal to Fetch API. To create AbortController object do the following:
var abortCtrl = new AbortController();
var signal = abortCtrl.signal(); // store the signal to be use in fetch request
To send the abort signal do following:
abortCtrl.abort()
Fetch request with signal parameter
Then we put signal as parameter in the fetch request, for example (replace api_key with your own key):
let url = "http://maps.openweathermap.org/maps/2.0/weather/TA2/{z}/{x}/{y}?date=1527811200&opacity=0.9&fill_bound=true&appid={api_key}";
let resp = await fetch(url,{signal})
let json = await resp.json();
When abort signal is received the fetch request will return reject promise with AbortError.
Adding timeout
OK now we want to have the capability if within specific amount of time the fetch request still not returning any data we want the timeout message cause we don’t want to wait any longer. For this purpose we can create two promises and running it in parallel and wait which ever promise finish first we will take the result.
let promise1 = fetch(someUrl, {signal});
let promise2 = new Promise(resolve=>{
setTimeout(async () => {
abortCtrl.abort() // abort fetch request
resolve("timeout")
}, 500) // timeout in 500ms
})
let result = await Promise.race([promise1, promise2]);
Above code will run promise1 and promise 2 in parallel. Promise.race methods will return only single promise result that finish the execution first. If promise1 finish first than promise 2 it will return the fetch data. If promise2 finish first it will cancel task promise1 and return string ‘timeout’.
Putting it all together
Let’s create new fetch function that has timeout parameter.
async function fetchWithTimeout(url, timeout) {
let ctrl = new AbortController()
let signal = ctrl.signal
let fetchData = fetch(url, {signal})
.then(resp => resp.json())
.then(json=>json)
.catch(err => {throw err}) // sent error stack trace
// set timeout
let timeOut = new Promise(resolve => {
setTimeout(async () => {
ctrl.abort() // abort fetch request
resolve("timeout")
}, timeout)
})
let result = await Promise.race([fetchData, timeOut])
return result
}
Limitations
It will work with GET method only and it will return json object/data. In next article we will modify the function so it can accept POST method and return other data type.