正如我們所知,fetch
會傳回一個承諾。而 JavaScript 通常沒有「中止」承諾的概念。那麼,我們要如何取消正在進行的 fetch
?例如,如果使用者在我們網站上的動作表示不再需要 fetch
。
有特別內建的物件可供此用途:AbortController
。它不僅可用來中止 fetch
,還能中止其他非同步任務。
用法非常簡單
AbortController 物件
建立控制器
let controller = new AbortController();
控制器是一個非常簡單的物件。
- 它有一個單一方法
abort()
, - 和一個單一屬性
signal
,允許在上面設定事件聆聽器。
當呼叫 abort()
時,
controller.signal
發出"abort"
事件。controller.signal.aborted
屬性變為true
。
通常,此程序中有兩方
- 執行可取消操作的一方會在
controller.signal
上設定監聽器。 - 取消的一方:在需要時呼叫
controller.abort()
。
以下是完整範例(尚未包含 fetch
)
let controller = new AbortController();
let signal = controller.signal;
// The party that performs a cancelable operation
// gets the "signal" object
// and sets the listener to trigger when controller.abort() is called
signal.addEventListener('abort', () => alert("abort!"));
// The other party, that cancels (at any point later):
controller.abort(); // abort!
// The event triggers and signal.aborted becomes true
alert(signal.aborted); // true
正如我們所見,AbortController
僅是呼叫其上的 abort()
時傳遞 abort
事件的方法。
我們可以在自己的程式碼中實作相同類型的事件監聽,而不需要 AbortController
物件。
但有價值的是,fetch
知道如何使用 AbortController
物件。它已整合在其中。
與 fetch 搭配使用
若要取消 fetch
,請將 AbortController
的 signal
屬性傳遞為 fetch
選項
let controller = new AbortController();
fetch(url, {
signal: controller.signal
});
fetch
方法知道如何使用 AbortController
。它會監聽 signal
上的 abort
事件。
現在,若要中止,請呼叫 controller.abort()
controller.abort();
我們完成了:fetch
從 signal
取得事件,並中止要求。
中止 fetch
時,其承諾會以 AbortError
錯誤拒絕,因此我們應該處理它,例如在 try..catch
中。
以下是 1 秒後中止 fetch
的完整範例
// abort in 1 second
let controller = new AbortController();
setTimeout(() => controller.abort(), 1000);
try {
let response = await fetch('/article/fetch-abort/demo/hang', {
signal: controller.signal
});
} catch(err) {
if (err.name == 'AbortError') { // handle abort()
alert("Aborted!");
} else {
throw err;
}
}
AbortController 可擴充
AbortController
可擴充。它允許一次取消多個 fetch
。
以下是同時擷取多個 url
並使用單一控制器中止所有 url
的程式碼草稿
let urls = [...]; // a list of urls to fetch in parallel
let controller = new AbortController();
// an array of fetch promises
let fetchJobs = urls.map(url => fetch(url, {
signal: controller.signal
}));
let results = await Promise.all(fetchJobs);
// if controller.abort() is called from anywhere,
// it aborts all fetches
如果我們有自己的非同步工作,不同於 fetch
,我們可以使用單一 AbortController
來停止這些工作,以及 fetch
。
我們只需在工作中監聽其 abort
事件
let urls = [...];
let controller = new AbortController();
let ourJob = new Promise((resolve, reject) => { // our task
...
controller.signal.addEventListener('abort', reject);
});
let fetchJobs = urls.map(url => fetch(url, { // fetches
signal: controller.signal
}));
// Wait for fetches and our task in parallel
let results = await Promise.all([...fetchJobs, ourJob]);
// if controller.abort() is called from anywhere,
// it aborts all fetches and ourJob
摘要
AbortController
是個簡單的物件,當呼叫abort()
方法時,會在其signal
屬性上產生abort
事件(也會將signal.aborted
設定為true
)。fetch
與其整合:我們將signal
屬性傳遞為選項,然後fetch
會監聽它,因此可以中止fetch
。- 我們可以在程式碼中使用
AbortController
。呼叫abort()
→ 監聽abort
事件的互動很簡單且通用。我們甚至可以在沒有fetch
的情況下使用它。
留言
<code>
標籤,若要插入多行程式碼,請將其包覆在<pre>
標籤中,若要插入超過 10 行的程式碼,請使用沙盒 (plnkr、jsbin、codepen…)