Вы действительно умеете пользоваться Fetch? Отмена запросов с AbortController
1. Fetch API — основа современных Web-запросов
Fetch API пришёл на смену устаревшему XMLHttpRequest и стал стандартным способом выполнения HTTP-запросов в браузере. Он основан на Promise и предоставляет более простой и мощный механизм запросов.
Однако по умолчанию Fetch API не предоставляет прямого механизма отмены запроса. Во многих ситуациях отмена запросов очень важна:
- Оптимизация производительности: Когда пользователь быстро переключает страницы или вводит поисковый запрос, отмена старых, уже ненужных запросов экономит пропускную способность и ресурсы сервера.
- Предотвращение состояний гонки: При поиске по мере ввода старый запрос может вернуться позже нового, из-за чего интерфейс покажет устаревшие данные.
- Предотвращение утечек памяти: Если компонент размонтирован до завершения запроса, запущенного в течение его жизненного цикла, попытка обновить уже не существующий компонент может вызвать ошибку или утечку памяти.
2. AbortController — универсальный сигнал отмены
AbortController — это универсальный Web API, предоставляющий механизм для прерывания одного или нескольких Web-запросов. Он не зависит от Fetch API, но прекрасно работает вместе с Fetch.
Основная идея AbortController такова:
- Создать экземпляр
AbortController. - Получить из него объект
AbortSignal. - Передать
AbortSignalWeb API, поддерживающему прерывание, напримерfetch. - Когда потребуется отмена, вызвать у экземпляра
AbortControllerметодabort().
3. Совместное использование AbortController и Fetch
Рассмотрим на примере, как с помощью AbortController отменить запрос Fetch.
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);При вызове controller.abort() запрос fetch немедленно прерывается и выбрасывает AbortError. Эту ошибку нужно перехватить в блоке catch и проверить error.name === 'AbortError', чтобы отличить отмену от других сетевых ошибок.
4. Практические сценарии
a. Поиск по мере ввода (Search-as-you-type)
Когда пользователь быстро печатает в строке поиска, каждый ввод может запускать новый запрос. Можно отменить предыдущий незавершённый запрос и оставить только самый новый.
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. Отмена запроса при размонтировании компонента
Во фреймворках вроде React или Vue отмена незавершённых запросов, запущенных внутри компонента, при его размонтировании помогает эффективно избежать утечек памяти и ненужных обновлений интерфейса.
// React 示例
useEffect(() => {
const controller = new AbortController();
fetchDataWithCancellation('/api/data', controller.signal);
return () => {
controller.abort(); // 组件卸载时取消请求
};
}, []);Заключение
AbortController — незаменимый инструмент современной Web-разработки. Он предоставляет нативный механизм отмены для Fetch API и других асинхронных операций, помогая создавать более надёжные и эффективные приложения с лучшим пользовательским опытом. Умение пользоваться AbortController — один из необходимых навыков хорошего фронтенд-разработчика.