AbortSignal
Baseline 廣泛可用 *
注意:此功能在 Web Workers 中可用。
AbortSignal 介面表示一個訊號物件,允許您與非同步操作(如 fetch 請求)進行通訊,並透過 AbortController 物件在需要時中止該操作。
例項屬性
還繼承了其父介面 EventTarget 的屬性。
AbortSignal.aborted只讀-
一個 布林值,指示訊號正在通訊的請求是否已被中止(
true)或未被中止(false)。 AbortSignal.reason只讀-
訊號中止後,提供中止原因的 JavaScript 值。
靜態方法
還繼承了其父介面 EventTarget 的方法。
AbortSignal.abort()-
返回一個已設定為已中止的
AbortSignal例項。 AbortSignal.any()-
返回一個
AbortSignal,當給定的任何中止訊號中止時,它將中止。 AbortSignal.timeout()-
返回一個
AbortSignal例項,該例項將在指定時間後自動中止。
例項方法
還繼承了其父介面 EventTarget 的方法。
AbortSignal.throwIfAborted()-
如果訊號已被中止,則丟擲訊號的中止
reason;否則不執行任何操作。
事件
還繼承了其父介面 EventTarget 的事件。
使用 addEventListener() 偵聽此事件,或將事件監聽器分配給此介面的 oneventname 屬性。
abort-
當訊號正在通訊的非同步操作被中止時呼叫。也可透過
onabort屬性訪問。
示例
使用顯式訊號中止 fetch 操作
以下程式碼片段展示瞭如何使用訊號透過 Fetch API 中止影片下載。
我們首先定義一個 AbortController 變數。
在每個 fetch 請求之前,我們使用 AbortController() 建構函式建立一個新的控制器,然後透過 AbortController.signal 屬性獲取對其關聯 AbortSignal 物件的引用。
注意: 一個 AbortSignal 只能使用一次。一旦它被中止,任何使用相同訊號的 fetch 呼叫都將立即被拒絕。
當 fetch 請求被髮起時,我們將 AbortSignal 作為請求選項物件中的一個選項(下面的 { signal })傳入。這會將訊號和控制器與 fetch 請求關聯起來,並透過呼叫 AbortController.abort() 來中止它,正如下面的第二個事件監聽器中所見。
當呼叫 abort() 時,fetch() Promise 會以一個名為 AbortError 的 DOMException 被拒絕。
let controller;
const url = "video.mp4";
const downloadBtn = document.querySelector(".download");
const abortBtn = document.querySelector(".abort");
downloadBtn.addEventListener("click", fetchVideo);
abortBtn.addEventListener("click", () => {
if (controller) {
controller.abort();
console.log("Download aborted");
}
});
async function fetchVideo() {
controller = new AbortController();
const signal = controller.signal;
try {
const response = await fetch(url, { signal });
console.log("Download complete", response);
// process response further
} catch (err) {
console.error(`Download error: ${err.message}`);
}
}
如果請求在 fetch() 呼叫已完成但響應體尚未讀取之前被中止,那麼嘗試讀取響應體會因 AbortError 異常而被拒絕。
async function get() {
const controller = new AbortController();
const request = new Request("https://example.org/get", {
signal: controller.signal,
});
const response = await fetch(request);
controller.abort();
// The next line will throw `AbortError`
const text = await response.text();
console.log(text);
}
您可以在 GitHub 上找到完整的可執行示例;您也可以 線上檢視。
使用超時中止 fetch 操作
如果您需要在超時時中止操作,可以使用靜態的 AbortSignal.timeout() 方法。它返回一個 AbortSignal,該訊號將在指定毫秒數後自動超時。
以下程式碼片段展示瞭如何成功下載檔案,或在 5 秒後處理超時錯誤。請注意,當發生超時時,fetch() Promise 會以一個 TimeoutError DOMException 被拒絕。這使得程式碼可以區分超時(可能需要使用者通知)和使用者中止。
const url = "video.mp4";
try {
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
const result = await res.blob();
// …
} catch (err) {
if (err.name === "TimeoutError") {
console.error("Timeout: It took more than 5 seconds to get the result!");
} else if (err.name === "AbortError") {
console.error(
"Fetch aborted by user action (browser stop button, closing tab, etc.",
);
} else {
// A network error, or some other problem.
console.error(`Error: type: ${err.name}, message: ${err.message}`);
}
}
使用超時或顯式中止來中止 fetch
如果您想從多個訊號中中止,可以使用 AbortSignal.any() 將它們合併成一個訊號。以下示例使用 fetch 展示了這一點。
try {
const controller = new AbortController();
const timeoutSignal = AbortSignal.timeout(5000);
const res = await fetch(url, {
// This will abort the fetch when either signal is aborted
signal: AbortSignal.any([controller.signal, timeoutSignal]),
});
const body = await res.json();
} catch (e) {
if (e.name === "AbortError") {
// Notify the user of abort.
} else if (e.name === "TimeoutError") {
// Notify the user of timeout
} else {
// A network error, or some other problem.
console.log(`Type: ${e.name}, Message: ${e.message}`);
}
}
注意: 與使用 AbortSignal.timeout() 不同,無法確定最終的中止是由超時引起的。
實現可中止的 API
需要支援中止的 API 可以接受一個 AbortSignal 物件,並利用其狀態在需要時觸發中止訊號處理。
基於 Promise 的 API 應該透過用 AbortSignal 的中止 reason 來拒絕任何尚未解決的 Promise 來響應中止訊號。例如,考慮以下 myCoolPromiseAPI,它接受一個訊號並返回一個 Promise。如果訊號已被中止,或者檢測到中止事件,Promise 會立即被拒絕。否則,它會正常完成,然後解析 Promise。
function myCoolPromiseAPI(/* …, */ { signal }) {
return new Promise((resolve, reject) => {
// If the signal is already aborted, immediately throw in order to reject the promise.
if (signal.aborted) {
reject(signal.reason);
return;
}
// Perform the main purpose of the API
// Call resolve(result) when done.
// Watch for 'abort' signals
signal.addEventListener("abort", () => {
// Stop the main operation
// Reject the promise with the abort reason.
reject(signal.reason);
});
});
}
API 隨後可以像這樣使用。請注意,AbortController.abort() 被呼叫來中止操作。
const controller = new AbortController();
const signal = controller.signal;
startSpinner();
myCoolPromiseAPI({ /* …, */ signal })
.then((result) => {})
.catch((err) => {
if (err.name === "AbortError") return;
showUserErrorMessage();
})
.then(() => stopSpinner());
controller.abort();
不返回 Promise 的 API 可能會以類似的方式做出反應。在某些情況下,吸收訊號可能是有意義的。
規範
| 規範 |
|---|
| DOM # interface-AbortSignal |
瀏覽器相容性
載入中…
另見
- Fetch API
- Jake Archibald 的 可中止的 Fetch