AbortSignal

Baseline 廣泛可用 *

此功能已成熟,可跨多種裝置和瀏覽器版本工作。它自 ⁨2018 年 4 月⁩ 起已在所有瀏覽器中可用。

* 此特性的某些部分可能存在不同級別的支援。

注意:此功能在 Web Workers 中可用。

AbortSignal 介面表示一個訊號物件,允許您與非同步操作(如 fetch 請求)進行通訊,並透過 AbortController 物件在需要時中止該操作。

EventTarget AbortSignal

例項屬性

還繼承了其父介面 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 會以一個名為 AbortErrorDOMException 被拒絕。

js
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 異常而被拒絕。

js
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 被拒絕。這使得程式碼可以區分超時(可能需要使用者通知)和使用者中止。

js
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 展示了這一點。

js
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。

js
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() 被呼叫來中止操作。

js
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

瀏覽器相容性

另見