Do You Really Know How to Use Fetch? Canceling Requests with AbortController
1. Fetch API: The Foundation of Modern Web Requests
The Fetch API has replaced the aging XMLHttpRequest as the standard way to make HTTP requests in the browser. Built on Promises, it offers a cleaner and more powerful request API.
However, the Fetch API does not provide a direct request-cancellation mechanism by default. Cancellation is very important in many situations:
- Performance optimization: When users quickly switch pages or enter search terms, canceling old requests that are no longer needed saves bandwidth and server resources.
- Avoiding race conditions: In a search-as-you-type scenario, an older request may return more slowly than a newer one, causing the UI to display stale data.
- Preventing memory leaks: If a component is unmounted before a request started during its lifecycle completes, an attempt to update a component that no longer exists may cause errors or memory leaks.
2. AbortController: A General-Purpose Cancellation Signal
AbortController is a general-purpose Web API that provides a mechanism for aborting one or more Web requests. It is independent of the Fetch API, but works perfectly with Fetch.
The core idea of AbortController is:
- Create an
AbortControllerinstance. - Obtain an
AbortSignalobject from that instance. - Pass the
AbortSignalto an abortable Web API such asfetch. - When cancellation is needed, call the
AbortControllerinstance’sabort()method.
3. Combining AbortController with Fetch
Let’s look at an example of using AbortController to cancel a Fetch request.
const controller = new AbortController();
const signal = controller.signal; // 获取信号对象
async function fetchDataWithCancellation(url) {
try {
console.log('Fetching data...');
const response = await fetch(url, { signal }); // 将信号传递给 fetch
const data = await response.json();
console.log('Data received:', data);
return data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch request was aborted.');
} else {
console.error('Fetch error:', error);
}
}
}
// 示例用法
const promise = fetchDataWithCancellation('https://jsonplaceholder.typicode.com/posts/1');
// 假设在 500 毫秒后,我们决定取消这个请求
setTimeout(() => {
controller.abort(); // 调用 abort() 方法取消请求
console.log('Request aborted by timeout.');
}, 500);When controller.abort() is called, the fetch request is immediately aborted and throws an AbortError. You need to catch this error in the catch block and check error.name === 'AbortError' to distinguish cancellation from other network errors.
4. Practical Use Cases
a. Search as You Type
When a user types quickly in a search box, every input event may trigger a new request. We can cancel the previous unfinished request and keep only the latest one.
let currentController = null;
document.getElementById('searchInput').addEventListener('input', (event) => {
if (currentController) {
currentController.abort(); // 取消上一个请求
}
currentController = new AbortController();
const signal = currentController.signal;
const query = event.target.value;
if (query.length > 2) {
fetchDataWithCancellation(`/api/search?q=${query}`, signal);
}
});b. Canceling a Request When a Component Unmounts
In frameworks such as React or Vue, canceling unfinished requests started inside a component when it unmounts can effectively prevent memory leaks and unnecessary UI updates.
// React 示例
useEffect(() => {
const controller = new AbortController();
fetchDataWithCancellation('/api/data', controller.signal);
return () => {
controller.abort(); // 组件卸载时取消请求
};
}, []);Conclusion
AbortController is an indispensable tool in modern Web development. It provides a native cancellation mechanism for the Fetch API and other asynchronous operations, helping developers build applications that are more robust, efficient, and pleasant to use. Mastering AbortController is one of the essential skills of an excellent frontend developer.