了解所有關於 Fetch API 的知識,這是一種基於 Promise 的異步網路請求的現代方法。

介紹 Fetch API

自從 IE5 在1998年釋出以來,我們在瀏覽器中有使用 XMLHttpRequest (XHR) 這個選項進行異步網路請求。

在幾年之後,GMail 和其他豐富的應用程式廣泛使用它,使得這種方法變得非常受歡迎,甚至需要一個名稱,AJAX

直接使用 XMLHttpRequest 一直都很麻煩,所以通常會被某些庫抽象化,而 jQuery 就有其自己的相關幫助方法:

  • jQuery.ajax()
  • jQuery.get()
  • jQuery.post()

它們對於使得這一切變得更加容易特別有著巨大的影響,尤其是在保證它們在舊版瀏覽器上的兼容性方面。

Fetch API 就是一種現代化的異步網路請求方法,並使用 Promises 作為其基礎。

Fetch 在主要的瀏覽器上有著良好的支援,除了 IE。

Fetch API 的瀏覽器支援

GitHub 提供的 polyfill https://github.com/github/fetch 讓我們可以在任何瀏覽器上使用 fetch

使用 Fetch

開始使用 Fetch 發送 GET 請求非常簡單:

fetch('/file.json')

現在你已經在使用 fetch:它會發送一個 HTTP 請求以獲取在同一域名下的 file.json 資源。

正如你所看到的,fetch 函數在全局的 window 作用域中是可用的。

現在讓我們讓它變得更有用,實際上看一下檔案的內容:

fetch('./file.json')
 .then(response => response.json())
 .then(data => console.log(data))

呼叫 fetch() 會返回一個 promise。然後我們可以通過使用該 promise 的 then() 方法來等待 promise 的解析。

該處理程式接收 fetch promise 的返回值,即一個 Response 物件。

我們將在下一節詳細介紹這個物件。

捕捉錯誤

由於 fetch() 返回一個 promise,我們可以使用該 promise 的 catch 方法來攔截在請求執行過程中發生的錯誤以及 then 回調中的處理:

fetch('./file.json')
.then(response => {
 //...
})
.catch(err => console.error(err))

捕捉錯誤的另一種方式是在第一個 then 回調中處理它們:

fetch('./file.json')
.then(response => {
 if (!response.ok) { throw Error(response.statusText) }
 return response
})
.then(response => {
 //...
})

Response 物件

通過 fetch() 調用返回的 Response 物件包含了有關請求和網路回應的所有資訊。

Metadata

headers

訪問 response 物件上的 headers 屬性可以看到請求返回的 HTTP 標頭:

fetch('./file.json').then(response => {
 console.log(response.headers.get('Content-Type'))
 console.log(response.headers.get('Date'))
})

status

這個屬性是一個整數數字,表示 HTTP 回應的狀態。

  • 101、204、205 或 304 是表示沒有內容的回應狀態
  • 200 到 299 (包含)是表示成功的回應狀態
  • 301、302、303、307 或 308 是表示重新導向的回應狀態
fetch('./file.json').then(response => console.log(response.status))

statusText

statusText 是表示回應的狀態訊息。如果請求成功,狀態是 OK

fetch('./file.json').then(response => console.log(response.statusText))

url

url 表示我們獲取的資源的完整 URL。

fetch('./file.json').then(response => console.log(response.url))

內容主體

一個回應會有一個內容主體,可以通過幾種方法來獲取:

  • text() 以字串方式返回回應主體
  • json()JSON 解析後的物件方式返回回應主體
  • blob()Blob 物件方式返回回應主體
  • formData() 以 FormData 物件方式返回回應主體
  • arrayBuffer()ArrayBuffer 物件方式返回回應主體

所有這些方法都返回一個 promise。例如:

fetch('./file.json')
 .then(response => response.text())
 .then(body => console.log(body))
fetch('./file.json')
 .then(response => response.json())
 .then(body => console.log(body))

這個也可以用 ES2017async 函數 來寫:

;(async () => {
 const response = await fetch('./file.json')
 const data = await response.json()
 console.log(data)
})()

Request 物件

Request 物件表示一個資源請求,通常使用 new Request() API 來創建。

例如:

const req = new Request('/api/todos')

Request 物件提供了幾個只讀屬性來檢查資源請求的細節,包括:

  • method:請求的方法(GET、POST 等等)
  • url:請求的 URL
  • headers:請求的標頭相關的 Headers 物件
  • referrer:請求的引用
  • cache:請求的快取模式(例如:預設、重新加載、不快取)

以及提供了幾個方法,包括 json()text()formData() 以處理請求的內容主體。

完整 API 可以在 https://developer.mozilla.org/docs/Web/API/Request 找到。

請求標頭

能夠設置 HTTP 請求標頭是很重要的,而 fetch 讓我們可以使用 Headers 物件來實現這一點:

const headers = new Headers()
headers.append('Content-Type', 'application/json')

或者:

const headers = new Headers({
 'Content-Type': 'application/json'
})

要將標頭附加到請求,我們使用 Request 物件,而不是傳遞 URL 給 fetch()

例如:

fetch('./file.json')

我們這樣做:

const request = new Request('./file.json', {
 headers: new Headers({
 'Content-Type': 'application/json'
 })
})
fetch(request)

Headers 物件不僅限於設置值,還可以查詢:

headers.has('Content-Type')
headers.get('Content-Type')

還可以刪除以前設置的標頭:

headers.delete('X-My-Custom-Header')

POST 請求

Fetch 還允許使用其他任何 HTTP 方法進行請求:POST、PUT、DELETE 或 OPTIONS。

在請求的 method 屬性中指定方法,並在標頭和請求主體中傳遞其他參數:

例如這是一個 POST 請求的範例:

const options = {
 method: 'post',
 headers: {
 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
 },
 body: 'name=Flavio&test=1'
}

fetch(url, options).catch(err => {
 console.error('Request failed', err)
})

如何取消 Fetch 請求

fetch 被引入之後幾年的時間裡,沒有辦法取消一個已經打開的請求。

現在我們可以通過引入 AbortControllerAbortSignal 這兩個泛型 API 來實現中止事件的通知。

要使用這個 API,只需將一個訊號(signal)作為 fetch 的參數傳遞:

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

fetch('./file.json', { signal })

你可以設定一個 5 秒的超時,在 fetch 請求開始後 5 秒時觸發中止事件以取消它:

setTimeout(() => controller.abort(), 5 * 1000)

方便的是,如果 fetch 已經返回,調用 abort() 將不會引起任何錯誤。

當中止訊號出現時,fetch 會以一個名為 AbortErrorDOMException 拒絕該 promise:

fetch('./file.json', { signal })
 .then(response => response.text())
 .then(text => console.log(text))
 .catch(err => {
 if (err.name === 'AbortError') {
 console.error('Fetch 已取消')
 } else {
 console.error('另一個錯誤', err)
 }
 })

尋找更多資訊?

處理網路的工作確實很難,不是嗎?你可能會發現 Axios 這個 JavaScript 函式庫更適合你的需求,它在 Fetch 的基礎上擁有一些額外的功能。快去看看吧!