Fetch 实现 abort

4,156 阅读2分钟

中断一个 fetch 请求,主要使用 AbortController 实现。

目前,浏览器端原生获取数据,发请求的主要是 XMLHttpRequestfetchXMLHttpRequest 作为元老级的存在, fetch 是在 ES6 中推出的更现代的 API。

XMLHttpRequest 本身是支持请求中断的(abort),以下是 XHR 中断的示例

const xhr = new XMLHttpRequest()
xhr.method = 'GET'
xhr.url = 'https://slowmo.glitch.me/5000'
xhr.open(method, url, true)
xhr.send()

// 中断请求
abortButton.addEventListener('click', () => xhr.abort())

fetch 设计之初是不支持中断的,开发者 2015 年在 GitHub 上提的 issue 还是 open 状态,以至于开发者们尝试在 fetch 规范外兼容或解决这个问题,其中就包括 cancelable-promises 及其他 hack 方法。

但是,现在有了通用的 AbortControllerAbortSignal API,这些 API 是由 DOM 标准规范 提供的,而不是由语言本身提供的。

什么是 AbortController?

image.png

正如 DOM 规范文档中所说

Though promises do not have a built-in aborting mechanism, many APIs using them require abort semantics. AbortController is meant to support these requirements by providing an abort() method that toggles the state of a corresponding AbortSignal object. The API which wishes to support aborting can accept an AbortSignal object, and use its state to determine how to proceed.

尽管 Promise 没有内置的中止机制,但是使用它们的许多 API 都需要中止语义。 AbortController 旨在通过提供可以切换相应 AbortSignal 对象状态的 abort() 方法来支持这些需求。 希望支持中止的 API 可以接受 AbortSignal 对象,并使用其状态来确定如何进行。

以下是 AbortController 的基本用法

// 创建 AbortController 的实例
const controller = new AbortController()
const signal = controller.signal

// 监听 abort 事件,在 controller.abort() 执行后执行回调打印 
signal.addEventListener('abort', () => {
    console.log(signal.aborted) // true
})

// 触发中断
controller.abort()

怎么使用 AbortController 中断 fetch 请求?

fetch 接受 AbortSignal 作为第二个参数的一部分。

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

// API 5s 后返回相应
// https://slowmo.glitch.me/5000 5000 代表 5s 后返回相应值

fetch('https://slowmo.glitch.me/5000', { signal })
    .then(r => r.json())
    .then(response => console.log(response))
    .catch(err => {
        if (err.name === 'AbortError') {
    	    console.log('Fetch was aborted')
        } else {
    	    console.log('Error', err)
        }
    })

// 在 2s 后中断请求,将触发 'AbortError'
setTimeout(() => controller.abort(), 2000)

触发 controller.abort() 会中断 fetch 的 request 和 response。相同的 AbortSignal(以上示例中的 signal )可用于中止多个获取请求。 AbortController 不仅适用于 fetch,可以适用到中止任意异步事件,比如可以实现一个可中断的promise

浏览器支持情况

除了 IE 以外,支持性还是可以的

caniuse.com/#feat=abort… developer.mozilla.org/zh-CN/docs/…

polyfill

www.npmjs.com/package/abo… www.npmjs.com/package/abo…

参考:

developers.google.com/web/updates… github.com/whatwg/fetc…