AbortSignal

基线 广泛可用

此功能已完善,可在多种设备和浏览器版本中使用。它已在浏览器中可用,自 2019 年 3 月.

注意:此功能在 Web 工作线程 中可用。

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() 构造函数创建一个中止控制器,然后使用 AbortController.signal 属性获取对其关联的 AbortSignal 对象的引用。

fetch 请求 初始化时,我们在请求的选项对象(下面的 {signal})中将 AbortSignal 作为选项传递。这将信号和控制器与 fetch 请求相关联,并允许我们通过调用 AbortController.abort() 来中止它。在下面,您可以看到 fetch 操作在第二个事件监听器中中止,该监听器在单击中止按钮 (abortBtn) 时触发。

当调用 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 应该通过拒绝任何未解决的 promise 并使用 AbortSignal 中止 reason 来响应中止信号。例如,考虑以下 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);
    }

    // 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

浏览器兼容性

BCD 表仅在浏览器中加载

另请参阅