大家好,我卡頌。
遙想數年前的一次面試,面試官問我:promise
有什麼缺點?
真是百思不得姐啊...
答案是:promise
一旦初始化,就不能中止。這是由promise
的實現決定的。
AbortSignal
的出現使promise
從語義上變為可中止的。並且,只要符合規範,所有非同步操作都能變為可中止的。
AbortSignal是什麼
AbortSignal
是個實驗性API
,不過相容性還不錯,而且polyfill
實現起來也不復雜。
AbortSignal
可以例項化一個訊號物件(signal object
)。
AbortController
可以例項化一個訊號物件的控制器。
就像遙控器可以發出訊號關電視一樣,AbortController
的例項可以控制中止訊號。
只要符合AbortSignal
的接入規範,任何非同步操作都能實現中止功能。
舉個例子,首先new
一個控制器例項:
// 控制器例項
const controller = new AbortController();
const signal = controller.signal;
其中signal
是控制器對應的訊號物件。
訊號物件可以監聽abort
事件,當訊號被中止時被觸發。
呼叫controller.abort()
方法後會中止訊號,此時signal.aborted
為true
。
// 監聽 abort 事件
signal.addEventListener('abort', () => {
console.log("訊號中止!")
});
// 控制器中止訊號
controller.abort();
console.log('是否中止:', signal.aborted);
如上程式碼呼叫後會依次列印:
- 訊號中止!
- 是否中止:true
在fetch中的應用
fetch API
已經整合了AbortSignal
。
只需要將controller
內的訊號物件作為signal
引數傳給fetch
:
const controller = new AbortController();
fetch(url, {
signal: controller.signal
});
當呼叫controller.abort()
後,fetch
的promise
會變為AbortError DOMException reject
:
fetch('xxxx', {
signal: controller.signal
}).then(() => {}, err => {
if (err.name == 'AbortError') {
// 中止訊號
} else {
// 其他錯誤
}
})
可以在此時處理中止後的操作。
這裡有個取消視訊下載Demo,可以看看fetch
如何配合AbortSignal
實現取消下載
與任何非同步操作結合
不僅是fetch
,任何非同步操作只要符合如下規範,都可以與AbortError
整合:
- 將
AbortSignal
(訊號物件)作為API
的signal
引數傳入 - 約定如果
API
返回的promise
變為AbortError DOMException reject
則代表操作被中止 - 如果
signal.aborted === true
則立刻讓promise
變為reject
- 觀測
AbortSignal
狀態的變化
如果API
應用場景比較複雜(比如需要考慮多執行緒通訊),文件中提供了一套基於訂閱釋出的abort-algorithms機制來完成步驟4。
總結
雖然AbortSignal
原理很簡單,但只要遵守接入規範,他的可擴充套件性是很強的。
比如,可以將一個signal
傳給多個符合規範的API
,就能用一個控制器中止多個API
的呼叫。
就像一個遙控器,同時操作家裡的空調、電視、洗衣機,你愛了麼?
歡迎加入人類高質量前端框架研究群,帶飛