当前位置: 首页 > news >正文

XHR与Fetch取消请求的方法及原理深度解析

在前端开发中,网络请求的取消机制是提升用户体验和资源利用率的关键技术。无论是用户快速切换页面、搜索框输入防抖还是超时处理,都需要可靠的请求取消方案。本文将详细对比XMLHttpRequest(XHR)和Fetch API的取消请求实现方式,剖析其底层原理,并总结实际开发中的最佳实践。

一、为什么需要取消请求?

在以下场景中,取消请求至关重要:

  • 用户交互中断:如用户在请求未完成时跳转页面、关闭弹窗或切换标签
  • 搜索联想优化:输入框快速输入时,取消前一次未完成的搜索请求
  • 超时控制:请求超过指定时间后自动取消,避免无效等待
  • 资源竞争处理:同一资源的多次请求,仅保留最新一次,取消之前的请求

未正确取消的请求会导致:

  • 不必要的网络带宽消耗
  • 无效的内存占用(响应数据处理)
  • 错误的状态更新(已过时的请求响应干扰当前页面状态)
  • 可能的内存泄漏(回调函数引用未释放)

二、XMLHttpRequest(XHR)的取消方式

XMLHttpRequest是传统的浏览器原生请求API,其取消机制相对直观,通过abort()方法实现。

1. 基本用法

// 创建XHR实例
const xhr = new XMLHttpRequest();// 初始化请求
xhr.open('GET', '/api/data', true);// 发送请求
xhr.send();// 取消请求(在需要的时候调用)
setTimeout(() => {if (xhr.readyState !== 4) { // 若请求未完成xhr.abort();console.log('请求已取消');}
}, 5000); // 5秒后自动取消

2. 取消后的状态变化

调用abort()后,XHR对象会发生以下变化:

  • readyState属性被设为0(UNSENT状态)
  • status属性被设为0
  • 触发abort事件,可以通过onabort回调处理
  • 不会触发loaderror事件
xhr.onabort = function() {console.log('请求被主动取消');
};xhr.onerror = function() {console.log('请求发生错误'); // 取消请求不会触发此事件
};

3. 底层原理

XHR的abort()方法直接与浏览器的网络层交互:

  1. 浏览器终止对应的网络请求,释放TCP连接
  2. 清空请求相关的缓冲区和事件队列
  3. 重置XHR对象的内部状态
  4. 触发abort事件通知应用层

这种机制由浏览器原生实现,能够彻底终止网络请求,避免资源浪费。

三、Fetch API的取消方式

Fetch API是ES6引入的现代请求API,基于Promise设计,其取消机制需要配合AbortController实现。

1. 基本用法

// 创建控制器
const controller = new AbortController();
// 获取信号对象
const signal = controller.signal;// 发起fetch请求,关联signal
fetch('/api/data', { signal }).then(response => response.json()).then(data => console.log('请求成功', data)).catch(error => {if (error.name === 'AbortError') {console.log('请求已取消');} else {console.log('请求发生错误', error);}});// 取消请求(在需要的时候调用)
setTimeout(() => {controller.abort();
}, 5000); // 5秒后自动取消

2. 取消后的状态变化

调用controller.abort()后:

  • signal.aborted属性变为true
  • signal对象触发abort事件
  • fetch返回的Promise立即 reject,错误名称为AbortError
  • 浏览器终止网络请求
// 监听取消事件
signal.addEventListener('abort', () => {console.log('请求被取消(通过signal监听)');
});

3. 底层原理

Fetch的取消机制基于观察者模式:

  1. AbortController创建可观察的signal对象
  2. Fetch请求订阅signal的状态变化
  3. 调用abort()时,signal状态更新并通知所有订阅者
  4. Fetch接收通知后,浏览器终止网络请求并 reject Promise

这种设计允许一个AbortController控制多个请求:

const controller = new AbortController();
const signal = controller.signal;// 多个请求共享同一个signal
fetch('/api/data1', { signal });
fetch('/api/data2', { signal });// 一次调用取消所有请求
controller.abort();

四、XHR与Fetch取消机制的核心差异

特性XMLHttpRequestFetch API
取消方法直接调用xhr.abort()通过AbortController.abort()
多请求控制需单独管理每个XHR实例一个控制器可取消多个请求
状态检测通过xhr.readyStatexhr.status通过signal.aborted属性
错误处理触发onabort事件Promise reject 错误AbortError
浏览器兼容性IE8+支持IE不支持,现代浏览器支持
与Promise集成需手动封装原生基于Promise
取消后资源释放浏览器直接终止浏览器直接终止

五、实际应用场景

1. 搜索框防抖取消请求

// 搜索框输入防抖,取消前一次请求
let abortController = null;function search(query) {// 取消上一次未完成的请求if (abortController) {abortController.abort();}// 创建新的控制器abortController = new AbortController();const { signal } = abortController;// 发起新请求fetch(`/api/search?q=${query}`, { signal }).then(response => response.json()).then(data => {console.log('搜索结果', data);abortController = null; // 清除引用}).catch(error => {if (error.name !== 'AbortError') {console.error('搜索失败', error);}abortController = null; // 清除引用});
}// 输入框事件监听
document.getElementById('search-input').addEventListener('input', (e) => {search(e.target.value);
});

2. 页面卸载时取消所有请求

// 管理所有请求的控制器
const requestControllers = new Set();// 封装带取消功能的fetch
function fetchWithCancel(url, options = {}) {const controller = new AbortController();const signal = controller.signal;requestControllers.add(controller);const promise = fetch(url, { ...options, signal }).finally(() => {requestControllers.delete(controller);});// 提供取消方法promise.cancel = () => controller.abort();return promise;
}// 页面卸载时取消所有请求
window.addEventListener('beforeunload', () => {requestControllers.forEach(controller => {controller.abort();});
});

3. XHR超时取消实现

function requestWithTimeout(url, timeout = 5000) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open('GET', url, true);// 超时处理const timer = setTimeout(() => {xhr.abort();reject(new Error('请求超时'));}, timeout);xhr.onload = () => {clearTimeout(timer);if (xhr.status >= 200 && xhr.status < 300) {resolve(xhr.responseText);} else {reject(new Error(`HTTP错误: ${xhr.status}`));}};xhr.onerror = () => {clearTimeout(timer);reject(new Error('网络错误'));};xhr.onabort = () => {clearTimeout(timer);reject(new Error('请求被取消'));};xhr.send();});
}

六、注意事项与最佳实践

  1. 及时清理资源:取消请求后,应清除相关的定时器、事件监听器和引用,避免内存泄漏

  2. 区分取消与错误:在错误处理中明确区分取消事件和其他错误,避免错误提示混淆

  3. 避免过度取消:合理设计取消时机,避免频繁取消和发起请求导致的性能损耗

  4. 兼容性处理

    • 对于需要支持IE的项目,优先使用XHR的abort()方法
    • 现代浏览器项目推荐使用Fetch+AbortController组合
    • 可使用polyfill为旧浏览器提供AbortController支持
  5. 结合状态管理:在React/Vue等框架中,可在组件卸载时取消相关请求:

// React组件卸载时取消请求示例
useEffect(() => {const controller = new AbortController();fetch('/api/data', { signal: controller.signal }).then(response => response.json()).then(data => setData(data));// 组件卸载时取消请求return () => controller.abort();
}, []);

七、总结

XHR和Fetch提供了不同的请求取消机制:

  • XHR通过实例方法abort()直接取消,适合简单场景和旧浏览器兼容
  • Fetch通过AbortController和signal实现,支持多请求管理,更符合现代异步编程模式

两种机制的底层原理都是通过浏览器终止网络请求,释放资源,但Fetch的设计更灵活,尤其在需要同时取消多个请求的场景下优势明显。

在实际开发中,应根据项目的浏览器支持范围和功能需求选择合适的方案,同时建立完善的请求管理机制,确保在用户交互、组件生命周期变化等场景下能够正确取消请求,提升应用的性能和稳定性。


文章转载自:

http://hUyvSuhp.gnkbf.cn
http://hctHgb9o.gnkbf.cn
http://iioHrcZz.gnkbf.cn
http://0woLbPEG.gnkbf.cn
http://Ps0AcEbE.gnkbf.cn
http://N4JaHHLS.gnkbf.cn
http://dJ1WTtVv.gnkbf.cn
http://kuGHYCAH.gnkbf.cn
http://H0G9CNle.gnkbf.cn
http://kW6C8pdb.gnkbf.cn
http://SLarX7YX.gnkbf.cn
http://xgU48O8c.gnkbf.cn
http://3qOxNDmm.gnkbf.cn
http://k58pFjaD.gnkbf.cn
http://UaGQPVqt.gnkbf.cn
http://KKrd1lDT.gnkbf.cn
http://kkCqWNZO.gnkbf.cn
http://mbi6Xfbs.gnkbf.cn
http://NEzujpg6.gnkbf.cn
http://c8lRVieF.gnkbf.cn
http://OvKjL7B7.gnkbf.cn
http://Gwmxzspo.gnkbf.cn
http://AaXEeHDe.gnkbf.cn
http://alcZso8g.gnkbf.cn
http://1v40ayM9.gnkbf.cn
http://6PzueUrV.gnkbf.cn
http://DX1kp75m.gnkbf.cn
http://N9eG60eh.gnkbf.cn
http://ArH9ErV9.gnkbf.cn
http://sxvIIAOM.gnkbf.cn
http://www.dtcms.com/a/382667.html

相关文章:

  • 除了 transformer 还有哪些 新的 神经网络架构
  • 鸿蒙NEXT的Web组件网络安全与隐私保护实践
  • D. Coprime
  • 利用python pandas库清洗病例处方清洗步骤
  • 数据库在并发访问时,不同隔离级别下脏读幻读问题
  • Python核心技术开发指南(065)——with语句
  • Python核心技术开发指南(064)——析构方法
  • 20250913-01: Langchain概念:Runnable可运行接口
  • 记一次谷歌语法获取路径 针对空白页面
  • Java GC:从GC Roots到分代设计的哲学
  • 一款4000℃高温材料设计方案及性能预测
  • 【leetcode】64. 最小路径和
  • 2.10组件间的通信
  • MinerU学习
  • 网络安全学习
  • 如何用 Rust 重写 SQLite 数据库(一):项目探索
  • Qwen3-80B-A3B混合注意力机制
  • OBS使用教程:OBS多路推流插件如何下载?如何安装使用?
  • 禁用 vscode 的终端的粘滞滚动
  • 人工智能通识与实践 - 人工智能概述
  • Symantec卸载
  • 第34章 AI在文娱与内容创作领域的应用
  • 学生信息管理系统(面向对象初步接触)
  • LangChain 中 Output Parsers 是什么?
  • Wolfspeed重组计划已确认
  • 【C++】继承机制深度解析:多继承与菱形继承
  • 如何用Maxscript在选择样条线顶点放置球体?
  • (LeetCode 面试经典 150 题) 190. 颠倒二进制位(位运算)
  • P1043题解
  • 如何用 Rust 重写 SQLite 数据库(二):项目探索