了解所有關於 Fetch API 的知識,這是一種基於 Promise 的異步網路請求的現代方法。
介紹 Fetch API
自從 IE5 在1998年釋出以來,我們在瀏覽器中有使用 XMLHttpRequest (XHR) 這個選項進行異步網路請求。
在幾年之後,GMail 和其他豐富的應用程式廣泛使用它,使得這種方法變得非常受歡迎,甚至需要一個名稱,AJAX。
直接使用 XMLHttpRequest 一直都很麻煩,所以通常會被某些庫抽象化,而 jQuery 就有其自己的相關幫助方法:
jQuery.ajax()
jQuery.get()
jQuery.post()
它們對於使得這一切變得更加容易特別有著巨大的影響,尤其是在保證它們在舊版瀏覽器上的兼容性方面。
Fetch API 就是一種現代化的異步網路請求方法,並使用 Promises 作為其基礎。
Fetch 在主要的瀏覽器上有著良好的支援,除了 IE。
由 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))
;(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
:請求的 URLheaders
:請求的標頭相關的 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
被引入之後幾年的時間裡,沒有辦法取消一個已經打開的請求。
現在我們可以通過引入 AbortController
和 AbortSignal
這兩個泛型 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 會以一個名為 AbortError
的 DOMException
拒絕該 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 的基礎上擁有一些額外的功能。快去看看吧!