fetch 方法到底取得的東西是什麼?
更新日期: 2024 年 10 月 30 日
在進行非同步網路請求時,fetch
是一個強大且靈活的工具。
使用 JavaScript 發送 HTTP 請求時,可以利用 fetch
來取得伺服器的資料。
但對初學者來說,fetch
究竟回傳了什麼?如何在 .then()
中使用這些回傳的內容?讓我們來釐清這些概念。
閱讀本文前,建議具備相關概念:初學者指南:了解 JavaScript 的 Promise 物件
fetch 回傳的內容:一個 Promise
當我們呼叫 fetch(api)
時,它會立即回傳一個 Promise
物件。
在 JavaScript 中,Promise
是用來處理非同步操作的一種機制,通常用於在資料取得後進行後續處理。
fetch
回傳的 Promise
並不會直接傳回 API 的資料,而是會在請求成功時自動 resolve 成一個 Response
物件,其中包含了請求結果的詳細資訊。
基本的 Promise 與 resolve 概念
在深入理解 fetch
的 Promise
之前,先掌握 resolve
的基本概念會對理解更有幫助。
當一個 Promise
被「resolve」時,它會將「resolve 時提供的資料」傳給 .then()
。
具體來說:
- 當呼叫
resolve
時,可以提供任意資料作為Promise
完成後的結果(例如字串、數字、物件,甚至是另一個Promise
)。 - 這些資料會被傳遞到
.then()
中 callback 函數的第一個參數,以供後續處理。
resolve 的範例
以下範例展示了 Promise
被 resolve 時,如何將資料傳遞給 .then()
:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data from the asynchronous operation"); // resolve 傳遞這個字串
}, 1000);
});
myPromise.then(result => {
console.log(result); // 顯示 "Data from the asynchronous operation"
});
在這個例子中:
- 當
setTimeout
完成後,resolve("Data from the asynchronous operation")
被呼叫,使Promise
狀態變為「已完成 (fulfilled)」。 resolve
傳入的字串"Data from the asynchronous operation"
,會作為結果傳給.then(result => {...})
中的result
參數,並在控制台輸出。
Response
物件是什麼?
這個 Response
物件包含了伺服器回應的詳細資訊,如:
回到 fetch
,當它的 Promise
被 resolve 時,並不會直接返回資料,而是返回一個 Response
物件。
fetch(api)
.then(response => {
console.log(response); // response 是一個 Response 物件
});
- 回應狀態碼(
status
)和回應是否成功的指示(ok
) - 回應的標頭(
headers
) - 回應的內容(這內容是未解析的原始格式)
因此,在 .then()
中接收到的 Response
物件,並不直接包含資料,而是提供了解析內容的方法。
如何從 Response
中取得實際資料?
Response
物件內不包含最終的資料,而是提供了解析的方式。常用的解析方法有:
response.json()
: 將內容解析為 JSON 格式response.text()
: 將內容解析為純文字response.blob()
: 將內容轉為二進制格式的 Blob
以下範例展示了如何解析 JSON 資料:
fetch(api)
.then(response => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json(); // 解析 JSON 格式的資料
})
.then(data => {
console.log(data); // 這裡的 data 是 JSON 解析後的實際資料
})
.catch(error => {
console.error("There was a problem with the fetch operation:", error);
});
在這個範例中:
fetch(api)
返回的Promise
自動 resolve,並傳回Response
物件。- 呼叫
response.json()
會將回應內容解析為 JSON,並返回一個新的Promise
。 - 該
Promise
resolve 後的資料會傳入下一個.then(data => {...})
的data
參數,這就是 API 回傳的最終資料。
補充:response.json()
vs JSON.parse(response)
response.json()
和 JSON.parse(response)
有著顯著的不同,主要是在處理方式和使用場合上。
response.json()
- 作用:
response.json()
是fetch
API 專用的方法,用來解析Response
物件中的 JSON 格式的資料。 - 返回:它會返回一個
Promise
,該Promise
resolve 後會提供解析後的 JavaScript 物件。
因此,在使用response.json()
後,你需要用.then()
來獲取解析後的資料。 - 適用場景:用於解析從
fetch
的Response
物件中提取的資料,適合處理 API 回應中的 JSON。
範例:
fetch(api)
.then(response => response.json()) // 返回一個解析後的 JSON 資料
.then(data => {
console.log(data); // 這裡的 data 是已解析的 JSON 物件
});
JSON.parse(response)
- 作用:
JSON.parse()
是一個 JavaScript 標準方法,用於將 JSON 格式的字串解析成 JavaScript 物件。 - 返回:它直接返回解析後的 JavaScript 物件,而不是一個
Promise
。 - 適用場景:用於已經是純字串格式的 JSON 資料。也就是說,如果你已經有一個 JSON 格式的字串(而不是
Response
物件),就可以使用JSON.parse()
來將其轉換為 JavaScript 物件。
範例:
const jsonString = '{"name": "John", "age": 30}';
const data = JSON.parse(jsonString);
console.log(data); // 這裡的 data 是已解析的 JSON 物件
差異總結
response.json()
適用於fetch
返回的Response
物件,而JSON.parse()
只能用於 JSON 格式的純字串。response.json()
返回的是Promise
,而JSON.parse()
直接返回解析後的物件。
因此,在 fetch
的回應中使用 response.json()
是更合適的選擇。
更詳細的過程
當我們執行這段 fetch
代碼時,解析資料的每個階段會在不同的時間點完成,以下是每個 .then()
的 Promise
resolve 時間點,並配有一張流程圖來清晰解釋整個過程。
流程圖
開始
↓
fetch(api) ➔ 返回 Promise
↓ (等待回應)
伺服器回應成功,Promise resolve,進入第一個 .then()
↓
response.json() ➔ 返回 Promise
↓ (開始解析 JSON)
JSON 解析完成,Promise resolve,進入下一個 .then()
↓
取得解析後的資料 (data),可以使用
↓
.catch() ➔ 如果有錯誤,進入這裡
- 第一個 .then:當
fetch
的Promise
被 resolve 後,我們進入第一個.then(response => response.json())
。
這時候,response.json()
開始解析 JSON 資料,但它會返回一個新的Promise
,因為解析資料是非同步的。 - 第二個 .then:這個新的
Promise
一旦完成 JSON 解析並 resolve,我們才能取得解析後的資料。因此,需要再加一個.then(data => {...})
來接收解析完成的資料data
。
所以整體來看,第一個 .then(response => response.json())
會返回一個 Promise
,而第二個 .then(data => {...})
才能接收解析後的最終資料。
代碼
fetch(api)
.then(response => response.json()) // response.json() 返回一個 Promise
.then(data => {
console.log(data); // 這裡的 data 是解析後的 JavaScript 物件
})
.catch(error => {
console.error("Error:", error);
});
每個階段的 resolve
時間點
fetch(api)
:fetch
發送請求並立即返回一個Promise
。- 時間點:
fetch
請求到伺服器回應成功時,這個Promise
會被 resolve,並返回一個Response
物件,進入第一個.then(response => response.json())
。
- 時間點:
response.json()
:response.json()
開始解析 JSON,並返回一個新的Promise
。- 時間點:當
response.json()
的解析完成時,這個Promise
會被 resolve,並返回解析後的 JavaScript 物件,進入下一個.then(data => {...})
。
- 時間點:當
.then(data => {...})
: 在這裡,我們可以取得解析後的資料data
。- 時間點:此
Promise
會在response.json()
解析完成並將資料提供給.then(data => {...})
後 resolve。
- 時間點:此
.catch(error => {...})
: 如果在fetch
請求或response.json()
解析過程中發生錯誤,程式會跳過.then()
並進入.catch(error => {...})
。- 時間點:當請求過程中有任何錯誤,會馬上觸發
.catch()
。
- 時間點:當請求過程中有任何錯誤,會馬上觸發
在這段代碼中,每個階段 resolve
的時間點取決於以下條件:
fetch
取得伺服器回應後,進入第一個.then(response => response.json())
。response.json()
解析完成後,進入下一個.then(data => {...})
,並提供解析後的 JavaScript 物件data
。
總結:fetch 到底取得什麼?
簡而言之,fetch
並不直接返回資料,而是通過 Promise
返回一個 Response
物件。
我們需要透過 Response
的解析方法(如 json()
)來提取出最終的資料內容。
這樣的設計讓 fetch
更加靈活,適合處理各種格式的回應資料,比如 JSON、文字和二進制檔案。
希望這篇文章讓你對 fetch
的運作有更清楚的理解!