前端如何取消接口调用
🧑💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣
1. xmlHttpRequest是如何取消请求的?
实例化的XMLHttpRequest对象上也有abort方法
1 2 3 4 5 6 | const xhr = new XMLHttpRequest(); xhr.addEventListener( 'load' , function(e) { console.log( this .responseText); }); xhr.open( 'GET' , 'https://jsonplaceholder.typicode.com/todos/1' ); xhr.send(); |
1 2 3 4 5 6 7 | // 返回 { "userId" : 1, "id" : 1, "title" : "delectus aut autem" , "completed" : false } |
如果在send后直接abort取消
1 2 | // xhr的取消操作:执行过程比较模糊,不知道abort什么时机进行处理 xhr.abort() |
2. AbortController
1 2 3 4 5 6 7 | const ac = new AbortController(); const { signal } = ac; const url = "https://jsonplaceholder.typicode.com/todos/1" ; fetch(url, { signal }) .then((res) => res.json()) .then((json) => console.log(json)); |
直接使用abort取消请求
1 | ac.abort() |
这里报错的原因是没有对错误进行捕获
1 2 3 4 5 6 | // 修改后的代码 fetch(url, { signal: ac.signal }) .then((res) => res.json()) .then((json) => console.log(json)) . catch (e => console.log(e)) // DOMException: signal is aborted without reason ac.abort() // abort接受一个入参,会被传递到signal的reason属性中 |
为什么可以这样取消?
fetch监听signal对象的状态,进而可以终止请求
2.1 如何同时取消多个请求?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | const ac = new AbortController(); const { signal } = ac; const url = "https://jsonplaceholder.typicode.com/todos" ; const todoRequest = (id, { signal }) => { fetch(`${url}/${id}`, { signal }) .then((res) => res.json()) .then((json) => console.log(json)) . catch ((e) => console.log(e)); // DOMException: signal is aborted without reason }; todoRequest(1, { signal }); todoRequest(2, { signal }); todoRequest(3, { signal }); ac.abort( "cancled" ); |
2.2 AbortSignal
是一个接口,用于表示一个信号对象,它允许你与正在执行的异步操作通信,以便可以在操作完成之前将其中止。
2.3 AbortSignal的方法
2.3.1 abort
静态方法,用于创建一个已经中止的
AbortSignal
对象。当你调用这个方法时,它会返回一个带有aborted
状态为true
的AbortSignal
实例。
1 | const signalAbout = AbortSignal.abort(); // AbortSignal {aborted: true, reason: DOMException: signal is aborted without reason...} |
2.3.2 throwIfAborted 方法
用于在执行代码之前检查
AbortSignal
是否已经被中止。如果AbortSignal
已经被中止,它会抛出一个AbortError
。这个方法可以帮助开发者在执行特定操作之前确保没有被中止,以避免不必要的处理。
1 2 3 4 5 6 | const signalAbout = AbortSignal.abort( 'abortedReason' ); try { signalAbout.throwIfAborted(); // 抛出error: abortedReason } catch (error) { console.log(error); } |
2.3.3 timeout
用于创建一个在指定时间后自动中止的
AbortSignal
对象。这在需要设置请求超时时非常有用。
1 2 3 4 5 6 7 8 9 | // 使用 AbortSignal.timeout 设置 10ms超时 const signalAbout = AbortSignal.timeout(10); const todoRequest = (id, { signal }) => { fetch(`${url}/${id}`, { signal }) .then((res) => res.json()) .then((json) => console.log( "json: " , json)) . catch ((e) => console.log( "err: " , e)); //DOMException: signal timed out }; todoRequest(1, { signal: signalAbout }); |
2.3.3.1 添加事件监听 => 从没有终止到被终止
AbortSignal继承自EventTarget,因为 AbortSignal 是用来监听 abort 事件的,而 EventTarget 提供了添加、移除和触发事件监听器的机制。
1 2 3 4 5 | const signalAbout = AbortSignal.timeout(10); signalAbout.addEventListener( "abort" , (e) => { console.log( "aborted: " , e); }) |
e的打印如下:
3. 实现一个主动取消的promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | const ac = new AbortController(); const { signal } = ac; const cancelablePromise = ({signal}) => new Promise((resolve, reject) => { // 情况1:直接主动取消 signal?.throwIfAborted(); // 也可以用reject // 情况2:正常处理业务逻辑 setTimeout(() => { Math.random() > 0.5 ? resolve( 'data' ) : reject( 'fetch error' ); }, 1000); // 情况3:超时 todo? // 监听取消 signal.addEventListener( "abort" , () => { reject(signal?.reason); }); }) // 发起网络请求 cancelablePromise({signal}) .then(res => console.log( 'res: ' , res)) . catch (err => console.log( 'err: ' , err)) // 情况1 // ac.abort('用户离开页面了') // err: 用户离开页面了 // 情况2 正常请求 err: fetch error || res: data |
4. 如何使用signal取消事件监听?
当对一个元素添加了多个事件监听,不需要像removeEventListener一样,每个事件都需要取消一次,每次都要写明对应事件的事件句柄
使用signal 只需要取消一次信号,全部事件监听都被取消
1 2 3 4 5 6 7 8 9 10 11 | const ac = new AbortController(); const { signal } = ac; const eleA = document.querySelector( '#a' ); const eleB = document.querySelector( '#b' ); function aEleHandler () {}; // 事件 eleA.addEventListener( 'click' , aEleHandler, {signal}); // 无论绑定多少个事件,都只需要一个signal eleB.addEventListener( 'click' , () => { ac.abort(); // 只需要取消一次 }) |
5. 请求多个接口进行数据组装的场景
当网速不好的时候,如何取消这种不断进行的网络请求?
1 2 3 4 5 6 7 8 9 10 11 12 | const ac = new AbortController(); const { signal } = ac; const fetchAndRenderAction = (signal) => { requestData(signal); // 多个串行或者并行的接口 drawAndRender(signal); // 异步渲染 } try { fetchAndRenderAction({signal}) } catch { // dosomething... } |
分类:
前端基础知识合集
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
2023-07-10 记录--关于浏览器缓存策略这件事儿