由於受 cancelable promise 的拖延,fetch 一直沒有傳統的 XHR 所擁有的 abort() 和 onprogress 功能,去年年末 cancelable promise 草案被完全廢棄了,因此今年年初的這幾個月裏,一份徹底新的、fetch 專用的、不依賴 JS 規範、與 Promise 沒有任何直接關係的草案誕生了,並且 Firefox 已經率先實現了其中的大部分功能。git
這份草案中引入了 3 種新的對象類型,FetchController(fetch 控制器)、FetchSignal(fetch 信號)、FetchObserver(fetch 觀察者),雖然這三個都是全局構造函數,但其實只有 FetchController 對象是真的須要你手動 new 出來的,其它兩種對象都是瀏覽器爲你生成的,咱們下面分別演示下如何中斷(取消、停止)一個 fetch 請求,以及如何監聽 fetch 請求的執行進度。github
const controller = new FetchController() const signal = controller.signal fetch("https://tc39.github.io/ecma262/", { signal }).then(() => { alert("請求成功") }).catch(err => { if (err.name === "AbortError") { alert("請求被中斷") } }) setTimeout(() => controller.abort(), 500)
這段代碼中,咱們先建立了一個 fetch 控制器 controller,每一個控制器都自帶一個 fetch 信號對象(signal 屬性),而後在你用 fetch() 發送請求的時候把這個信號對象帶上(signal 參數)。以後,你就能夠經過控制器的 abort() 方法,來中斷那個 fetch 請求了。只要在你執行 abort() 方法的時候,那個 fetch 請求尚未執行完畢(請求還沒發出去、請求發出去了還沒接收到響應、響應還沒接收完),那麼當初 fetch() 返回的那個 promise 就會被 reject,所產生的 error 對象的 name 爲 AbortError。canvas
上面這個例子中,我是在 500 毫秒的時候中斷了這個請求,這是個絕對的數字,因此根據你的網速不一樣,執行代碼時你有可能會看到彈出「請求成功」,也有能看到「請求被中斷」。promise
一個信號對象還能夠同時傳遞給多個 fetch 請求:瀏覽器
const controller = new FetchController() const signal = controller.signal fetch("https://tc39.github.io/ecma262/", { signal }) fetch("https://fetch.spec.whatwg.org/", { signal }) fetch("https://dom.spec.whatwg.org/", { signal }) controller.abort() // 同時中斷三個請求
信號對象有一個 aborted 屬性代表本身是否已經被中斷過了,一旦被中斷,它不可能回到當初的狀態,也就是說,你不能二次利用一個已經被中斷過的信號對象,把這樣的信號對象傳給 fetch() 的話,fetch() 會當即結束,不會發出請求:dom
const controller = new FetchController() const signal = controller.signal controller.abort() alert(signal.aborted) // true fetch("https://tc39.github.io/ecma262/", { signal }) // 直接被 reject,請求不會發出
信號對象上還能夠監聽 abort 事件,不過我想通常用不上,由於請求中斷後的處理代碼通常寫在 fetch(...).catch(...) 裏面:函數
const controller = new FetchController() const signal = controller.signal signal.addEventListener("abort", function(e) { // 使用 signal.onabort = 註冊監聽函數也能夠 alert(e.type) // abort alert(signal.aborted) // true }) alert(signal.aborted) // false controller.abort()
一個控制器除了可讓本身的信號對象 abort,還能夠關注(跟隨、監聽)一個別的控制器的信號對象,只要關注的這個信號對象被 abort 了,本身的 abort() 方法就會被自動執行,從而本身的信號對象也會被 abort:fetch
const fc1 = new FetchController() const fc2 = new FetchController() fc1.follow(fc2.signal) fc2.abort() alert(fc1.signal.aborted) // true,即使你沒有手動執行 fc1.abort()
再來看個更復雜的例子:spa
const fc1 = new FetchController() const fc2 = new FetchController() const fc3 = new FetchController() fc1.follow(fc2.signal) fc2.follow(fc3.signal) fetch("https://tc39.github.io/ecma262/", { signal: fc1.signal }) fetch("https://fetch.spec.whatwg.org/", { signal: fc2.signal }) fetch("https://dom.spec.whatwg.org/", { signal: fc3.signal }) fc3.abort() // 三個請求都中斷了
一個控制器只能關注一個別人家的信號對象,而一個信號對象能夠被任意多個別家的控制器關注。不能直接或間接的關注本身的信號對象,好比上面的代碼中再增長一行 fc3.follow(fc1.signal) 就會產生環形關注,因此那句代碼會沒有任何效果。還有一個 unfollow() 方法能夠取消關注。code
總結下就是,一個控制器對象有一個 signal 屬性指向一個信號對象,還有 abort()、follow()、unfollow() 這三個方法。一個信號對象有一個 aborted 屬性,還有一個 onabort 事件監聽函數。
上面說到了 FetchController 和 FetchSignal,接下來要說說 fetch 觀察者 - FetchObserver 對象。fetch 觀察者對象上能夠監聽 statechange、requestprogress、responseprogress 三種事件,分別用來獲取 fetch 請求的狀態信息變化、請求數據發送的大小變化(POST 請求)、響應數據接收的大小變化信息,先來看看如何監聽狀態信息的變化:
fetch("https://tc39.github.io/ecma262/", { observe(observer) { observer.onstatechange = () => { // 也能夠用 addEventListener("statechange"... alert(observer.state) // "requesting"、"responding"、"aborted", "errored"、"complete" 中的某個值 } } })
FetchObserver 對象不用你手動 new,瀏覽器會爲你 new 一個,在 fetch 請求開始以前,經過你事先指定的 observe 回調函數的參數傳遞給你。而後你在這個觀察者對象上註冊 statechange 監聽函數,就能夠在請求的不一樣階段執行特定的代碼了。
監聽響應的接收進度使用 responseprogress 事件:
fetch("https://img.alicdn.com/tfscom/TB1y6icQXXXXXaQXFXXXXXXXXXX.mp4", { observe(observer) { observer.onresponseprogress = e => { if(e.lengthComputable) { console.log("視頻下載進度:" + parseInt(e.loaded / e.total * 100) + "%") } } } })
監聽 POST 數據的發送進度使用 requestprogress 事件:
fetch("/upload", { method: "POST",
credentials: "include", body: canvas.toBlob(), observe(observer) { observer.onrequestprogress = e => { console.log("圖片上傳進度:" + parseInt(e.loaded / e.total * 100) + "%") } } })
在本文發佈的如今,規範草案還只是最初的階段,可能有不少細節沒考慮,目前 Firefox 也沒有實現我上面所說的全部功能,實現了的也有一些 bug,並且還須要事先在 about:config 手動添加某兩個選項纔可使用,因此本文僅僅是簡要介紹,你們暫時眼睛看看就夠了。
之後 fetch 控制器的功能可能還會增長,不單單能讓一個 fetch 請求中斷,好比還能夠指定這個請求的優先級,fetch 觀察者同時也能夠用來監聽 fetch 請求優先級的變化。