I currently have a list of coordinates, which i use to fetch nearby hikes. Lets say I update my list state to new coordinates, but the fetch requests for the previous coordinates were not complete-- How can i cancel all the fetch requests made for the previous coordinates and only call new fetch requests based on the new coordinates?
I tried implementing AbortController but its probably not working as it's not cancelling a "batch" of fetch requests, but only single fetch request. My code is below:
let abortController = new AbortController();
const getNearbyHikes = async (coordinates) => {
abortController.abort();
abortController = new AbortController();
for (const coord of coordinates) {
if (coord) {
try {
const { lat, lng } = cord.coordinates
const response = await fetch(
'http://localhost:5000/category/' + lat + "/" + lng + '/' + searchCategory,
{signal: abortController.signal}
)
const result = await response.json();
setHikes((prevState) => [...prevState, ...result.businesses])
} catch (error) {
if(error.name === 'AbortError'){
return;
}
throw error
}
}
}
}
Also , I want to update my UI with the hikes as I get them so it doesnt seem like my web app is really slow so I avoid using Promise.all. Any suggestions or help is greatly appreciated.
The main problem with the cancellation is that you're providing the signal to fetch incorrectly. You're providing the signal as a second argument, but fetch expects an "init" object there with a signal property on it.
As @Keith points out, though, your current code is making one call at a time. That will work, but you probably would benefit from doing the calls in parallel.
Something like this:
const getOneResult = async ({ lat, lng }, signal) => {
try {
const response = await fetch(
"http://localhost:5000/category/" +
lat +
"/" +
lng +
"/" +
searchCategory, // *** Where does this come from?
{ signal }
);
if (signal && signal.aborted) {
throw new DOMException("Request cancelled", "AbortError");
}
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return response.json();
} catch (error) {
if (error.name === "AbortError") {
return null;
}
}
};
const getNearbyHikes = async (coordinates, signal) => {
controller.abort();
controller = new AbortController();
const results = await Promise.all(
coordinates.map((coord) => getOneResult(coord.coordinates, signal))
);
if (signal && signal.aborted) {
return;
}
const businesses = [];
for (const result of results) {
businesses.push(...result.businesses);
}
setHikes((prevState) => [...prevState, ...businesses]);
};
Finally, I would have the caller supply the signal rather than using a global one for all calls to getHikes:
const getOneResult = async ({ lat, lng }, signal) => {
try {
const response = await fetch(
"http://localhost:5000/category/" +
lat +
"/" +
lng +
"/" +
searchCategory, // *** Where does this come from?
{ signal }
);
if (signal && signal.aborted) {
throw new DOMException("Request cancelled", "AbortError");
}
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return response.json();
} catch (error) {
if (error.name === "AbortError") {
return null;
}
}
};
let controller = new AbortController();
const getNearbyHikes = async (coordinates) => {
controller.abort();
controller = new AbortController();
const results = await Promise.all(
coordinates.map((coord) => getOneResult(coord.coordinates, signal))
);
if (signal && signal.aborted) {
return;
}
const businesses = [];
for (const result of results) {
businesses.push(...result.businesses);
}
setHikes((prevState) => [...prevState, ...businesses]);
};
then the caller controls aborting the request. But the former may be fine for your use case.
Side note: You'll see I've added a check for response.ok in there. Your code was assuming that since the fetch promise was fulfilled, the HTTP call worked, but unfortunately that's a footgun in the fetch API: it only rejects its promise on network failure, not HTTP failure. You have to check for the latter explicitly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With