此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。
AbortSignal
Baseline Widely available *
This feature is well established and works across many devices and browser versions. It’s been available across browsers since 2018年4月.
* Some parts of this feature may have varying levels of support.
备注: 此特性在Web Worker 中可用。
AbortSignal 接口表示一个信号对象,它允许你通过AbortController 对象与异步操作(如 Fetch 请求)进行通信并在需要时将其中止。
In this article
实例属性
也继承了其父接口EventTarget 的属性。
AbortSignal.aborted只读一个
Boolean,表示与之通信的请求是否被中止(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 请求开始时,我们将AbortSignal 作为一个选项传递进请求的 option 对象中(见下面的{signal})。这个将信号和控制器与 fetch 请求相关联,并且允许我们通过调用AbortController.abort() 中止它。你可以看见当中止按钮(abortBtn)被点击时,第二个事件监听器触发,使 fetch 操作被中止。
当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("下载已中止"); }});async function fetchVideo() { controller = new AbortController(); const signal = controller.signal; try { const response = await fetch(url, { signal }); console.log("下载完毕", response); // 进一步处理请求 } catch (err) { console.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(); // 下一行代码将抛出 `AbortError` const text = await response.text(); console.log(text);}你可以在 GitHub 上找到一个完整、可运行的示例;你也可以参见在线演示。
中止超时的读取操作
如果你需要中止一个超时的操作,你可以使用AbortSignal.timeout() 静态方法。该方法返回一个在指定的毫秒时间后后自动超时的AbortSignal。
以下代码片段展示了如何成功地下载一个文件或者在五秒钟后处理一个超时的错误。注意,当出现超时时,fetch() promise 会以TimeoutErrorDOMException 拒绝。这允许代码区分超时(可能需要通知用户)和用户手动中止操作。
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("超时:获取结果的事件超过了 5 秒!"); } else if (err.name === "AbortError") { console.error("Fetch 操作被用户中止(如浏览器停止按钮、关闭标签等)"); } else { // 网络错误,或其他问题 console.error(`错误:类型:${err.name},消息:${err.message}`); }}超时或显式中止 fetch
如果要从多个信号中中止,可以使用AbortSignal.any() 将它们合并为一个信号。下面的示例使用fetch 进行了演示:
try { const controller = new AbortController(); const timeoutSignal = AbortSignal.timeout(5000); const res = await fetch(url, { // 任意一个信号中止时,整个操作会被中止 signal: AbortSignal.any([controller.signal, timeoutSignal]), }); const body = await res.json();} catch (e) { if (e.name === "AbortError") { // 通知用户操作中止 } else if (e.name === "TimeoutError") { // 通知用户超时 } else { // 网络错误,或其他问题 console.log(`类型:${e.name},消息:${e.message}`); }}备注:与使用AbortSignal.timeout() 不同,没有方法来判断最终中止是否由超时引起。
实现可中止的 API
需要支持中止的 API 可以接受一个AbortSignal 对象,并在需要时使用其状态来触发中止信号处理。
基于Promise 的 API 应对中止信号做出响应,拒绝任何未确定的承诺,并使用AbortSignal 中止reason。例如,请看下面的myCoolPromiseAPI,它接收一个信号并返回一个 promise。如果信号已被中止,或检测到中止事件,则会立即拒绝该 promise。否则,它将正常完成,然后兑现 promise。
function myCoolPromiseAPI(/* …, */ { signal }) { return new Promise((resolve, reject) => { // 如果信号已经被中止,立即抛出错误,以拒绝 promise if (signal.aborted) { reject(signal.reason); } // 执行 API 主要的目标 // 当完成时调用 resolve(result) // 监听 'abort' 信号 signal.addEventListener("abort", () => { // 停止主要任务 // 以中止理由拒绝 promise 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 接口可能会以类似的方式做出反应。在某些情况下,接收信号可能是有意义的。
规范
| Specification |
|---|
| DOM> # interface-AbortSignal> |
浏览器兼容性
参见
- Fetch API
- 可中止的 Fetch,来自 Jake Archibald