iT邦幫忙

0

【30 天JavaScript 實戰 】 Day 17 |fetch+Promise 的錯誤處理

  • 分享至 

  • xImage
  •  

Day 16 我們自己寫 new Promise((resolve, reject) => { ... })
但在實務上,我們比較常遇到的是:

const p = fetch("/api/data"); // fetch 會直接給一個 Promise

所以我們會少用 new Promise,改用 fetch 當例子喔!


今日的目標:


1. fetch 跟 Promise 的關係是什麼?

const p = fetch("/api/data");

這行做了兩件事:

  • 送出 HTTP 請求(去跟伺服器要資料)
  • 立刻回傳一個 Promise 物件(那張收據)

所以可以接著寫:

fetch("/api/data")
  .then((res) => {
    console.log("第一個 then 拿到:", res);
  })
  .catch((err) => {
    console.log("錯誤:", err);
  });

這裡 res 是一個 Response 物件(裡面有 status、ok、headers 等等)

2.fetch 什麼時候會 reject?

fetch 只有在「網路層出問題」時才會 reject,例如:

  • 使用者根本沒網路
  • 網域打錯
  • 被 CORS 擋掉
  • request 完全送不出去

這種情況才會進 .catch(err)

那 404、500 會怎樣?

fetch("/api/not-found") // 404
 .then((res) => {
   console.log("這裡還是會執行,status =", res.status); // 404
 })
 .catch((err) => {
   console.log("只有連線層級錯誤才會到這裡");
 });

也就是說:
對 fetch 來說,只要「伺服器有回應」,不管是 200 還是 500,
都算是一個「fulfilled 的 Promise」。

3. 那要怎麼知道 API 成功還是失敗?

答案是 要自己判斷 res.ok!

最標準的 fetch 寫法

fetch("/api/data")
  .then((res) => {
    // 這裡只是代表「伺服器有回應」,不代表 API 成功
    if (!res.ok) {
      // 這裡才是「API 失敗」的判斷
      throw new Error(`HTTP 錯誤:${res.status}`);
    }

    // 真正成功才進來
    return res.json(); // 再回傳一個 Promise
  })
  .then((data) => {
    console.log("成功拿到資料:", data);
  })
  .catch((err) => {
    console.error("整個流程的錯誤都會到這裡:", err);
  });
  • if (!res.ok) throw new Error(...)中的throw 會讓當前這個 Promise 變成 rejected,
    所以後面的 .catch(err) 就可以接到~
  • 成功的時候才 return res.json(),再進下一個 then!

4.鏈式操作進階版:完整寫一條 fetch 流程

完整 fetch 實務流程:

fetch("/api/user")
  .then((res) => {
    if (!res.ok) throw new Error(`讀取 user 失敗:${res.status}`);
    return res.json();
  })
  .then((user) => {
    // 這裡拿到 user 資料
    console.log("user:", user);
    // 用 user.id 再去抓他的貼文
    return fetch(`/api/users/${user.id}/posts`);
  })
  .then((res) => {
    if (!res.ok) throw new Error(`讀取 posts 失敗:${res.status}`);
    return res.json();
  })
  .then((posts) => {
    console.log("posts:", posts);
  })
  .catch((err) => {
    // 中間任何一個地方有 throw 或 fetch 網路層失敗,都會到這裡
    console.error("整條流程出錯:", err);
  });
  1. 先抓 user(要判斷 res.ok)
  2. 拿到 user.id 之後,再抓該 user 的 posts(再次判斷 res.ok)
  3. 中間任何一個地方錯誤,都集中到下面的 catch

5.錯誤恢復(Error Recovery)

有些情況下,錯了也可以「撿回來」繼續走!
當錯誤不是「致命錯誤」,而是「可以用替代方案繼續運作」的時候,就會在 catch 裡 return 正常值。
就算 API 失敗,如果不想整個畫面便錯誤畫面,
我們可以顯示預設資料、或是提示用戶「目前使用預設值」,這時候可以在 catch 裡「把錯誤轉成正常資料」:

fetch("/api/user")
 .then((res) => {
   if (!res.ok) throw new Error("讀取 user 失敗");
   return res.json();
 })
 .catch((err) => {
   console.warn("讀取失敗,用預設資料取代:", err.message);
   return { name: "Guest", role: "visitor" }; // 回傳正常值
 })
 .then((user) => {
   // 不論 API 成功或失敗,這裡都會執行
   console.log("最後拿到的 user:", user);
 });
  • catch 裡如果 return 一個正常值 → 後面的 then 會當作「錯誤已經處理好了」,繼續走下去
  • 如果 catch 裡再 throw 一次 → 錯誤會繼續往下傳,直到被下一個 catch 接住

舉例:
在首頁載入
使用者資料(重要)、推薦商品(次要)
所以如果推薦商品 API 爆掉,不應該讓整個首頁壞掉。

fetch("/api/recommend")
 .catch(err => {
   return [];  // 推薦商品掛了 → 顯示空清單即可
 });

使用者看起來會覺得:「沒推薦商品而已,不影響整體。」


/images/emoticon/emoticon06.gif


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言