使用 Fetch API 時實作錯誤處理

Umar Hansa
Umar Hansa

本文說明使用 Fetch API 時會發生的一些錯誤處理方法。Fetch API 可讓您向遠端網路資源提出要求。當您發出遠端網路呼叫時,網頁可能會發生各種潛在的網路錯誤。

以下各節將說明潛在錯誤,並說明如何編寫程式碼來提供靈活等級的功能,以抵禦錯誤和非預期的網路狀況。具有彈性的程式碼除了能滿足使用者的需求,也能維持網站的標準服務水準。

預測潛在網路錯誤

本節說明在以下情境中,使用者建立名為 "My Travels.mp4" 的新影片,然後嘗試將影片上傳到影片分享網站。

使用 Fetch 時,您可輕鬆思考使用者成功上傳影片的快樂路徑。不過,有些路徑也不太順暢,而是網頁程式開發人員必須針對這類路徑制定規劃。這類 (不愉快) 路徑的原因可能是使用者錯誤、未預期的環境條件,或影片分享網站發生錯誤。

使用者錯誤的例子

  • 使用者上傳的是圖片檔 (例如 JPEG) ,而非影片檔案。
  • 使用者開始上傳錯誤的影片檔案。然後,在上傳過程中,使用者可指定要上傳的正確影片檔案。
  • 使用者在上傳影片時不小心按 [取消上傳]。

環境變化範例

  • 影片上傳期間,網際網路連線會中斷。
  • 影片上傳期間,瀏覽器會重新啟動。
  • 影片分享網站伺服器會在影片上傳期間重新啟動。

影片分享網站錯誤的例子

  • 影片分享網站無法處理含有空格的檔案名稱。而不是 "My Travels.mp4",而不是 "My_Travels.mp4""MyTravels.mp4" 之類的名稱。
  • 影片分享網站無法上傳超出系統允許檔案大小上限的影片。
  • 影片分享網站不支援上傳影片中的視訊轉碼器。

這些例子在實際環境中的可能發生。您或許過去也遇到了這類錯誤!讓我們從先前的每個類別中挑選一個範例,然後討論以下幾點:

  • 如果影片分享服務無法處理指定的範例,預設行為為何?
  • 此範例中的使用者預期會發生什麼情況?
  • 該如何改善流程?
動作 使用者開始上傳錯誤的影片檔案。然後,在上傳過程中,使用者可指定要上傳的正確影片檔案。
預設動作 原始檔案會繼續在背景上傳,並同時上傳新檔案。
使用者的期望 使用者預期原始上傳會停止,以免浪費額外的網際網路頻寬。
哪些地方需要改進 在新檔案開始上傳之前,JavaScript 會取消原始檔案的擷取要求。
動作 使用者在上傳影片中途中斷網路連線。
預設動作 上傳進度列一直停在 50%。最後,Fetch API 會發生逾時情況,並捨棄上傳的資料。網際網路連線恢復後,使用者必須重新上傳檔案。
使用者的期望 使用者希望在檔案無法上傳時收到通知,並期望在檔案恢復連線後,在 50% 時自動繼續上傳。
哪些地方需要改進 上傳頁面會通知使用者網際網路連線問題,並向使用者保證會在網際網路連線恢復後繼續上傳。
動作 影片分享網站無法處理含有空格的檔案名稱。而不是「My Travels.mp4」,而不是「My_Travels.mp4」或「MyTravels.mp4」。
預設動作 使用者必須等待上傳完成。檔案上傳完成,且進度列顯示「100%」時,進度列會顯示「請再試一次」。
使用者的期望 使用者預期在開始上傳之前,或至少在上傳前一秒內收到檔案名稱限制。
哪些地方需要改進 影片分享服務最好支援帶有空格的檔案名稱。你也可以在開始上傳前,通知使用者檔案名稱限制。或者,影片分享服務應拒絕上傳,並包含詳細的錯誤訊息。

使用 Fetch API 處理錯誤

請注意,下列程式碼範例使用頂層 await (瀏覽器支援),因為這項功能可以簡化程式碼。

Fetch API 擲回錯誤時

這個範例使用 try/catch 區塊陳述式,擷取 try 區塊中擲回的任何錯誤。例如,如果 Fetch API 無法擷取指定資源,就會擲回錯誤。請在這類 catch 區塊中,提供「有意義的」使用者體驗。如果旋轉圖示是代表某種進度的常見使用者介面,且已向使用者顯示,您可以在 catch 區塊內執行以下操作:

  1. 移除頁面上的旋轉圖示。
  2. 提供實用訊息,說明問題所在,以及使用者可採取的選項。
  3. 根據可用的選項,向使用者顯示「再試一次」按鈕。
  4. 在背景,將錯誤的詳細資料傳送到錯誤追蹤服務或後端。這個動作會記錄錯誤,以供日後診斷。
try {
  const response = await fetch('https://website');
} catch (error) {
  // TypeError: Failed to fetch
  console.log('There was an error', error);
}

您也可以在之後診斷您所記錄的錯誤時,撰寫測試案例,在使用者發現錯誤前找出這類錯誤。視錯誤而定,測試可以是單元、整合或接受測試。

聯播網狀態碼代表錯誤時

這個程式碼範例向 HTTP 測試服務發出要求,且一律回應 HTTP 狀態碼 429 Too Many Requests。有趣的是,回應不會到達 catch 區塊。404 狀態 (以及其他某些狀態碼) 不會傳回網路錯誤,而會正常解析。

如要檢查 HTTP 狀態碼是否成功,您可以使用下列任一選項:

  • 使用 Response.ok 屬性判斷狀態碼是否介於 200299 之間。
  • 使用 Response.status 屬性判斷回應是否成功。
  • 使用任何其他中繼資料 (例如 Response.headers) 評估回應是否成功。
let response;

try {
  response = await fetch('https://httpbin.org/status/429');
} catch (error) {
  console.log('There was an error', error);
}

// Uses the 'optional chaining' operator
if (response?.ok) {
  console.log('Use the response here!');
} else {
  console.log(`HTTP Response Code: ${response?.status}`)
}

最佳做法是與貴機構和團隊中的人員合作,瞭解潛在的 HTTP 回應狀態碼。後端開發人員、開發人員作業人員和服務工程師有時會提供獨特的深入分析資料,讓您瞭解您可能預期的極端情況。

剖析網路回應時發生錯誤

這個程式碼範例說明剖析回應主體可能會引發的另一種錯誤。Response 介面提供便利的方法,可用來剖析文字或 JSON 等不同類型的資料。下列程式碼會發出網路要求,讓 HTTP 測試服務傳回 HTML 字串做為回應內文。不過,系統會嘗試將回應主體剖析為 JSON,進而擲回錯誤。

let json;

try {
  const response = await fetch('https://httpbin.org/html');
  json = await response.json();
} catch (error) {
  if (error instanceof SyntaxError) {
    // Unexpected token < in JSON
    console.log('There was a SyntaxError', error);
  } else {
    console.log('There was an error', error);
  }
}

if (json) {
  console.log('Use the JSON here!', json);
}

您必須準備好要採用各種回應格式的程式碼,並確認非預期的回應不會對網頁造成損害。

請考量以下情境:您擁有會傳回有效 JSON 回應的遠端資源,且該資源已透過 Response.json() 方法成功剖析。服務可能會停止運作。降級後,系統會傳回 500 Internal Server Error。如未在剖析 JSON 期間使用適當的錯誤處理技術,就會因為擲回未處理的錯誤,中斷使用者的網頁。

何時必須先取消網路要求才能完成

這個程式碼範例使用 AbortController 取消傳輸中要求。執行中的要求是指已啟動但尚未完成的網路要求。

可能需要取消執行中的要求各有不同,但最終取決於您的用途和環境。下列程式碼示範如何將 AbortSignal 傳遞至 Fetch API。AbortSignal 會附加至 AbortController,而 AbortController 包含 abort() 方法,會向瀏覽器表明應取消網路要求。

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

// Cancel the fetch request in 500ms
setTimeout(() => controller.abort(), 500);

try {
  const url = 'https://httpbin.org/delay/1';
  const response = await fetch(url, { signal });
  console.log(response);
} catch (error) {
  // DOMException: The user aborted a request.
  console.log('Error: ', error)
}

結論

處理錯誤的重要部分是定義可能發生錯誤的各個部分。無論何時,請務必為使用者設定適當的備用方案。您可以針對擷取要求提出下列問題:

  • 如果目標伺服器當機,會發生什麼情況?
  • 如果擷取作業收到未預期的回應,會發生什麼情況?
  • 如果使用者網路連線失敗,會發生什麼情況?

視網頁的複雜程度而定,您也可以草擬流程圖,說明不同情境的功能和使用者介面。