Canceling Duplicate HTTP Requests in Front-End Development
The article explains how front‑end developers can prevent duplicate or stale HTTP calls by identifying identical requests with a unique key, then canceling them using Axios’s CancelToken or Fetch’s AbortController—integrated via React hooks, Axios interceptors, and route‑change cleanup.
As front‑end developers we often deal with CRUD API calls, but duplicate or stale HTTP requests can cause unnecessary network traffic and race conditions.
This article discusses how to cancel duplicate HTTP requests at the request level rather than relying on UI debouncing.
Two common scenarios are identified: (1) identical GET/POST requests with the same method, URL, parameters or body, and (2) route changes that render previous requests irrelevant.
Implementation consists of two steps: detecting duplicate requests and cancelling them. The article demonstrates solutions for both Axios and Fetch.
Axios uses its internal CancelToken. A CancelToken is created via axios.CancelToken.source() and passed to the request. The cancellation can be triggered in a cleanup function, e.g., inside a React useEffect hook.
useEffect(() => {
const cancelToken = axios.CancelToken;
const source = cancelToken.source();
setAxiosRes("axios request created");
getReq(source).then((res) => {
setAxiosRes(res);
});
return () => {
source.cancel("axios request cancelled");
};
}, [axiosClick]);An Axios instance and request helper are defined as:
export const instance = axios.create({
baseURL: "http://localhost:4001",
});
export const getReq = async (source) => {
try {
const { data } = await instance.get("/", {
cancelToken: source.token,
});
return data;
} catch (err) {
if (axios.isCancel(err)) {
return "axios request cancelled";
}
return err;
}
};Fetch relies on the AbortController and its signal . The controller is aborted in the cleanup function of useEffect .
export const instance = axios.create({
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
setFetchRes("fetch request created");
hitApi(signal).then((res) => {
setFetchRes(res);
});
// cleanup function
return () => {
controller.abort();
};
}, [fetchClick]);The hitApi helper passes the signal to fetch and handles the AbortError :
export const hitApi = async (signal) => {
try {
const response = await fetch("http://localhost:4001/", {
signal
});
const data = await response.json();
return data;
} catch (err) {
if (err.name === "AbortError") {
return "Request Aborted ";
}
return err;
}
};To identify duplicates efficiently, a Map (named pending ) stores a unique key composed of method, URL, serialized params and data. Two utility functions manage this map:
const addPending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data)
].join('&');
config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
if (!pending.has(url)) {
pending.set(url, cancel);
}
});
};
const removePending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data)
].join('&');
if (pending.has(url)) {
const cancel = pending.get(url);
cancel(url);
pending.delete(url);
}
};These functions are typically added to Axios request and response interceptors:
axios.interceptors.request.use(config => {
removePending(options); // cancel previous duplicate
addPending(options); // add current request
return config;
}, error => Promise.reject(error));
axios.interceptors.response.use(response => {
removePending(response);
return response;
}, error => {
if (axios.isCancel(error)) {
console.log('repeated request: ' + error.message);
} else {
// handle other errors
}
return Promise.reject(error);
});When a route change occurs, all pending requests can be cleared:
export const clearPending = () => {
for (const [url, cancel] of pending) {
cancel(url);
}
pending.clear();
};
router.beforeEach((to, from, next) => {
clearPending();
// ...
next();
});The article also explains the inner workings of Axios’s CancelToken and the xhrAdapter , showing how the promise attached to the token is observed and aborts the underlying XHR when cancelled.
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve; // expose resolve
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
if (config.cancelToken) {
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
request = null;
});
}
// ... actual XHR logic
});
}In conclusion, cancelling duplicate HTTP requests can be achieved by combining request identification with cancellation mechanisms provided by Axios and Fetch, integrated via React hooks and Axios interceptors.
DeWu Technology
A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.