fetch 的 JSON 只能讀一次?把解析結果留下來重用

API 明明回了資料,第一次 response.json() 也成功,為甚麼交給另一個函式再讀一次就報錯?Response 的 body 是串流,不是可以反覆打開的 JSON 檔。需要多處使用同一份資料時,先解析一次,再傳遞解析後的值。

不用連 API,也能重現第二次讀取失敗

以下用合成資料建立 Response,不會對外發送請求。在支援 Fetch API 的瀏覽器 Console,或支援 Response 的 Node.js 環境執行即可。

(async () => {
  const response = new Response('{"items":[11,13]}');
  console.log(response.bodyUsed); // false
  const data = await response.json();
  console.log(data.items);        // [11, 13]
  console.log(response.bodyUsed); // true
  try {
    await response.json();
  } catch (error) {
    console.log(error.name);      // TypeError
  }
})();

本例在 Node.js 24.14.1 執行,第二次讀取得到 TypeError。不同執行環境的完整錯誤文字可能不同,別只依賴某一句英文判斷。json() 會讀完 body 並解析;bodyUsed 可用來觀察是否已讀取,但把它改回 false 不能重置串流,它是唯讀屬性。

先解析,再把資料交給各個函式

若兩個區塊都需要 items,讓兩者接收 data,而不是各自接收 Response 並讀取它。下面仍用合成回應,方便直接比較。

(async () => {
  const response = new Response('{"items":[11,13]}');
  const data = await response.json();
  const itemCount = data.items.length;
  const firstItem = data.items[0];
  console.log(itemCount, firstItem); // 2, 11
})();

實際網站可以把 data 傳給列表與計數函式。這不會再次消耗 body,也不用為了修錯誤重送網路請求。解析成功仍不代表欄位正確,接外部 API 時應另外檢查資料形狀;多個函式共用物件時,也要避免互相修改它的內容。

確實要兩份 body,必須先 clone

例如小型回應同時需要解析結果與原始文字,可以在任何讀取開始之前複製 Response。

(async () => {
  const response = new Response('{"items":[11,13]}');
  const copy = response.clone();
  const data = await response.json();
  const text = await copy.text();
  console.log(data.items.length, text); // 2 與原始 JSON 文字
})();

已用過 body 才呼叫 clone() 仍會得到 TypeError。clone 也不是免費的大檔案備份:兩個分支消耗速度不同時,較慢的一側可能累積尚未讀取的內容;若只讀其中一側,另一側可能把整份內容留在記憶體。一般畫面共用解析結果時,優先採上一節的寫法,不要對所有回應一律 clone。

JSON 格式錯誤是另一件事

json() 也可能因內容不是合法 JSON 而產生 SyntaxError。先分清是第一次解析失敗,還是 body 已被前面的程式讀走。可從請求封裝、除錯紀錄及呼叫順序查起,尤其留意某個工具是否已先執行 text() 或 json()。

若真的要檢查原始文字,應在自有測試資料上安排讀取方式,不要把登入資料、token 或客戶內容直接寫進 Console 或日誌。本文的三組範例只驗證合成 Response 的讀取行為,不涵蓋正式 API 的權限與錯誤處理。

封面為 AI 生成的概念插圖。

參考資料

原文鏈接:https://wntheme.com/javascript-fetch-response-json-read-once/,轉載請註明出處。
0
顯示驗證碼
沒有帳號?註冊  忘記密碼?