🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
1. xmlHttpRequest是如何取消請求的?
例項化的XMLHttpRequest物件上也有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的取消操作:執行過程比較模糊,不知道abort什麼時機進行處理 xhr.abort()
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取消請求
ac.abort()
這裡報錯的原因是沒有對錯誤進行捕獲
// 修改後的程式碼 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 如何同時取消多個請求?
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
例項。
const signalAbout = AbortSignal.abort(); // AbortSignal {aborted: true, reason: DOMException: signal is aborted without reason...}
2.3.2 throwIfAborted 方法
用於在執行程式碼之前檢查
AbortSignal
是否已經被中止。如果AbortSignal
已經被中止,它會丟擲一個AbortError
。這個方法可以幫助開發者在執行特定操作之前確保沒有被中止,以避免不必要的處理。
const signalAbout = AbortSignal.abort('abortedReason'); try { signalAbout.throwIfAborted(); // 丟擲error: abortedReason } catch (error) { console.log(error); }
2.3.3 timeout
用於建立一個在指定時間後自動中止的
AbortSignal
物件。這在需要設定請求超時時非常有用。
// 使用 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 提供了新增、移除和觸發事件監聽器的機制。
const signalAbout = AbortSignal.timeout(10); signalAbout.addEventListener("abort", (e) => { console.log("aborted: ", e); })
e的列印如下:
3. 實現一個主動取消的promise
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 只需要取消一次訊號,全部事件監聽都被取消
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. 請求多個介面進行資料組裝的場景
當網速不好的時候,如何取消這種不斷進行的網路請求?
const ac = new AbortController(); const { signal } = ac; const fetchAndRenderAction = (signal) => { requestData(signal); // 多個序列或者並行的介面 drawAndRender(signal); // 非同步渲染 } try{ fetchAndRenderAction({signal}) }catch{ // dosomething... }
6. 總結
對於使用者主動離開頁面,或者使用者的網路很卡的時候(預期返回順序是:介面1 => 介面2;但是介面1返回太慢,導致順序混亂。)這就需要手動終止請求。建構函式AbortController的例項訊號量signal(可以作為ref儲存起來),signal作為fetch的引數,在每次請求的時候,可以手動呼叫abort方法,取消上一次的請求。
如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。