Do You Really Know How to Use Fetch? Canceling Requests with AbortController

3 min

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:

  1. Create an AbortController instance.
  2. Obtain an AbortSignal object from that instance.
  3. Pass the AbortSignal to an abortable Web API such as fetch.
  4. When cancellation is needed, call the AbortController instance’s abort() 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.