前端取消接口調(diào)用的方法示例
1. xmlHttpRequest是如何取消請(qǐng)求的?
實(shí)例化的XMLHttpRequest對(duì)象上也有abort方法
const xhr = new XMLHttpRequest(); xhr.addEventListener('load', function(e) { console.log(this.responseText); }); xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1'); xhr.send();
// 返回 { "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }
如果在send后直接abort取消
// xhr的取消操作:執(zhí)行過程比較模糊,不知道abort什么時(shí)機(jī)進(jìn)行處理 xhr.abort()
如果在定時(shí)器中(當(dāng)定時(shí)器的時(shí)長(zhǎng)那個(gè)和接口請(qǐng)求時(shí)長(zhǎng)差不多)取消請(qǐng)求,會(huì)發(fā)現(xiàn)資源已經(jīng)獲取到了,但是控制臺(tái)沒有打印
2. AbortController
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取消請(qǐng)求
ac.abort()
這里報(bào)錯(cuò)的原因是沒有對(duì)錯(cuò)誤進(jìn)行捕獲
// 修改后的代碼 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接受一個(gè)入?yún)?,?huì)被傳遞到signal的reason屬性中
為什么可以這樣取消?
fetch監(jiān)聽signal對(duì)象的狀態(tài),進(jìn)而可以終止請(qǐng)求
2.1 如何同時(shí)取消多個(gè)請(qǐng)求?
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
是一個(gè)接口,用于表示一個(gè)信號(hào)對(duì)象,它允許你與正在執(zhí)行的異步操作通信,以便可以在操作完成之前將其中止。
2.3 AbortSignal的方法
2.3.1 abort
靜態(tài)方法,用于創(chuàng)建一個(gè)已經(jīng)中止的 AbortSignal 對(duì)象。當(dāng)你調(diào)用這個(gè)方法時(shí),它會(huì)返回一個(gè)帶有 aborted 狀態(tài)為 true 的 AbortSignal 實(shí)例。
const signalAbout = AbortSignal.abort(); // AbortSignal {aborted: true, reason: DOMException: signal is aborted without reason...}
2.3.2 throwIfAborted 方法
用于在執(zhí)行代碼之前檢查 AbortSignal 是否已經(jīng)被中止。如果 AbortSignal 已經(jīng)被中止,它會(huì)拋出一個(gè) AbortError。這個(gè)方法可以幫助開發(fā)者在執(zhí)行特定操作之前確保沒有被中止,以避免不必要的處理。
const signalAbout = AbortSignal.abort('abortedReason'); try { signalAbout.throwIfAborted(); // 拋出error: abortedReason } catch (error) { console.log(error); }
2.3.3 timeout
用于創(chuàng)建一個(gè)在指定時(shí)間后自動(dòng)中止的 AbortSignal 對(duì)象。這在需要設(shè)置請(qǐng)求超時(shí)時(shí)非常有用。
// 使用 AbortSignal.timeout 設(shè)置 10ms超時(shí) 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 添加事件監(jiān)聽 => 從沒有終止到被終止
AbortSignal繼承自EventTarget,因?yàn)?AbortSignal 是用來監(jiān)聽 abort 事件的,而 EventTarget 提供了添加、移除和觸發(fā)事件監(jiān)聽器的機(jī)制。
const signalAbout = AbortSignal.timeout(10); signalAbout.addEventListener("abort", (e) => { console.log("aborted: ", e); })?
e的打印如下:
3. 實(shí)現(xiàn)一個(gè)主動(dòng)取消的promise
const ac = new AbortController(); const { signal } = ac; ? const cancelablePromise = ({signal}) => new Promise((resolve, reject) => { // 情況1:直接主動(dòng)取消 signal?.throwIfAborted(); // 也可以用reject ? // 情況2:正常處理業(yè)務(wù)邏輯 ? setTimeout(() => { Math.random() > 0.5 ? resolve('data') : reject('fetch error'); }, 1000); ? // 情況3:超時(shí) todo? ? // 監(jiān)聽取消 signal.addEventListener("abort", () => { reject(signal?.reason); }); }) // 發(fā)起網(wǎng)絡(luò)請(qǐng)求 cancelablePromise({signal}) .then(res => console.log('res: ', res)) .catch(err => console.log('err: ', err)) // 情況1 // ac.abort('用戶離開頁面了') // err: 用戶離開頁面了 ? // 情況2 正常請(qǐng)求 err: fetch error || res: data
4. 如何使用signal取消事件監(jiān)聽?
當(dāng)對(duì)一個(gè)元素添加了多個(gè)事件監(jiān)聽,不需要像removeEventListener一樣,每個(gè)事件都需要取消一次,每次都要寫明對(duì)應(yīng)事件的事件句柄
使用signal 只需要取消一次信號(hào),全部事件監(jiān)聽都被取消
const ac = new AbortController(); const { signal } = ac; const eleA = document.querySelector('#a'); const eleB = document.querySelector('#b'); ? function aEleHandler () {}; // 事件 eleA.addEventListener('click', aEleHandler, {signal}); // 無論綁定多少個(gè)事件,都只需要一個(gè)signal ? eleB.addEventListener('click', () => { ac.abort(); // 只需要取消一次 })
5. 請(qǐng)求多個(gè)接口進(jìn)行數(shù)據(jù)組裝的場(chǎng)景
當(dāng)網(wǎng)速不好的時(shí)候,如何取消這種不斷進(jìn)行的網(wǎng)絡(luò)請(qǐng)求?
const ac = new AbortController(); const { signal } = ac; const fetchAndRenderAction = (signal) => { requestData(signal); // 多個(gè)串行或者并行的接口 drawAndRender(signal); // 異步渲染 } ? try{ fetchAndRenderAction({signal}) }catch{ // dosomething... }
6. 總結(jié)
對(duì)于用戶主動(dòng)離開頁面,或者用戶的網(wǎng)絡(luò)很卡的時(shí)候(預(yù)期返回順序是:接口1 => 接口2;但是接口1返回太慢,導(dǎo)致順序混亂。)這就需要手動(dòng)終止請(qǐng)求。構(gòu)造函數(shù)AbortController的實(shí)例信號(hào)量signal(可以作為ref存儲(chǔ)起來),signal作為fetch的參數(shù),在每次請(qǐng)求的時(shí)候,可以手動(dòng)調(diào)用abort方法,取消上一次的請(qǐng)求。
到此這篇關(guān)于前端取消接口調(diào)用的文章就介紹到這了,更多相關(guān)前端取消接口調(diào)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript計(jì)算出兩個(gè)數(shù)的差值
這篇文章主要為大家詳細(xì)介紹了JavaScript計(jì)算出兩個(gè)數(shù)的差值,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03使用js實(shí)現(xiàn)的簡(jiǎn)單拖拽效果
本文給大家分享的是使用純JS實(shí)現(xiàn)的簡(jiǎn)單的拖拽效果的插件,算是對(duì)自己javascript學(xué)習(xí)的一個(gè)小的檢驗(yàn),如果小伙伴們需要復(fù)雜的拖拽效果,還是考慮jQuery的draggable吧,更成熟一些。2015-03-03JavaScript日期和時(shí)間的格式化及其它常用處理方法
這篇文章主要給大家介紹了關(guān)于JavaScript日期和時(shí)間的格式化及其它常用處理方法,JavaScript中可以使用Date對(duì)象來表示日期和時(shí)間,如果需要格式化日期和時(shí)間,可以使用Date對(duì)象的幾個(gè)方法和一些字符串操作方法來實(shí)現(xiàn),需要的朋友可以參考下2023-09-09JavaScript實(shí)現(xiàn)刪除數(shù)組重復(fù)元素的5種常用高效算法總結(jié)
這篇文章主要介紹了JavaScript實(shí)現(xiàn)刪除數(shù)組重復(fù)元素的5種常用高效算法,結(jié)合實(shí)例形式總結(jié)分析了javascript刪除數(shù)組重復(fù)元素的幾種常見操作技巧,需要的朋友可以參考下2018-01-01echarts餅圖標(biāo)簽formatter使用及餅圖自定義標(biāo)簽
項(xiàng)目中有遇到需要使用餅圖展示每種狀態(tài)所占比例,去echarts官網(wǎng)學(xué)習(xí)了一番,下面這篇文章主要給大家介紹了關(guān)于echarts餅圖標(biāo)簽formatter使用及餅圖自定義標(biāo)簽的相關(guān)資料,需要的朋友可以參考下2022-12-12