AbortSignal

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2019.

AbortSignal 接口表示一个信号对象(signal object),它允许你通过 AbortController 对象与 DOM 请求(如 Fetch)进行通信并在需要时将其中止。

属性

AbortSignal 接口还继承了其父接口 EventTarget 的属性。

AbortSignal.aborted 只读

一个 Boolean,表示与之通信的请求是否被终止(true)或未终止(false)。

AbortSignal.reason 只读

一旦信号被中止,提供一个使用 JavaScript 值表示的中止原因。

方法

AbortSignal 接口继续它父接口 EventTarget 的方法。

AbortSignal.throwIfAborted()

如果信号已经被中止,则抛出信号中止的 reason;否则没有任何用处。

静态方法

AbortSignal.abort()

返回一个已经被设置为中止的 AbortSignal 实例。

AbortSignal.timeout()

返回一个在指定时间后自动终止的 AbortSignal 实例。

事件

使用 addEventListener() 或将事件监听器分配给该接口的 oneventname 属性。

abort

当与 signal 通信 DOM 请求被中止时调用。也可以通过 onabort 属性调用。

示例

使用显式的信号终止 fetch 操作

以下片段展示了我们如何使用 signal 去中止使用 Fetch API 下载视频。

我们首先使用 AbortController() 创建一个中止控制器,然后使用 AbortController.signal 属性获取与它关联的 AbortSignal 对象的引用。

fetch 请求开始时,我们将 AbortSignal 作为一个选项传递进请求的 option 对象中(见下面的 {signal})。这个将信号和控制器与 fetch 请求相关联,并且允许我们通过调用 AbortController.abort() 中止它。你可以看见当中止按钮(abortBtn)被点击时,第二个事件监听器触发,使 fetch 操作被中止。

js
const controller = new AbortController();
const signal = controller.signal;

const url = "video.mp4";
const downloadBtn = document.querySelector(".download");
const abortBtn = document.querySelector(".abort");

downloadBtn.addEventListener("click", fetchVideo);

abortBtn.addEventListener("click", () => {
  controller.abort();
  console.log("Download aborted");
});

function fetchVideo() {
  fetch(url, { signal })
    .then((response) => {
      console.log("Download complete", response);
    })
    .catch((err) => {
      console.error(`Download error: ${err.message}`);
    });
}

备注: 当调用 abort() 时,fetch() promise 会以“AbortErrorDOMException 拒绝。

你可以在 GitHub 上找到一个完整、可运行的示例;你也可以参见在线演示

中止超时的读取操作

如果你需要中止一个超时的操作,你可以使用 AbortSignal.timeout() 静态方法。该方法返回一个 AbortSignal 并在指定的毫秒时间后后自动超时。

以下代码片段展示了如何成功地下载一个文件或者在五秒钟后处理一个超时的错误。注意,当出现超时时,fetch() promise 会以“TimeoutErrorDOMException 拒绝。这允许代码区分超时(可能需要通知用户)和用户中止。

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 if (err.name === "TypeError") {
    console.error("AbortSignal.timeout() method is not supported");
  } else {
    // A network error, or some other problem.
    console.error(`Error: type: ${err.name}, message: ${err.message}`);
  }
}

超时或显式中止 fetch

fetch() 不是为了组合多个信号而设计的,所以你不能因为调用了 AbortController.abort() 或者 AbortSignal 超时而"直接"中止下载(尽管在上面的例子中,超时信号中止由停止按钮等内置浏览器机制触发)。

为了触发多个 signal,它们必须是菊花链式连接。以下代码片段展示了在处理程序中如何为独立的计时器调用 AbortController.abort()

js
try {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 5000);
  const res = await fetch(url, { signal: controller.signal });
  const body = await res.json();
} catch (e) {
  if (e.name === "AbortError") {
    // Notify the user of abort.
    // Note this will never be a timeout error!
  } else {
    // A network error, or some other problem.
    console.log(`Type: ${e.name}, Message: ${e.message}`);
  }
} finally {
  clearTimeout(timeoutId);
}

备注: 与使用 AbortSignal.timeout() 不同,没有方法来判断最终中止是否由超时引起。

规范

Specification
DOM Standard
# interface-AbortSignal

浏览器兼容性

BCD tables only load in the browser

参见