Fetch 實現 abort

Vincent.W發表於2020-04-04

中斷一個 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…

相關文章