正如我們所知,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…)