Day 16 我們自己寫 new Promise((resolve, reject) => { ... })
但在實務上,我們比較常遇到的是:
const p = fetch("/api/data"); // fetch 會直接給一個 Promise
所以我們會少用 new Promise,改用 fetch 當例子喔!
今日的目標:
const p = fetch("/api/data");
這行做了兩件事:
所以可以接著寫:
fetch("/api/data")
.then((res) => {
console.log("第一個 then 拿到:", res);
})
.catch((err) => {
console.log("錯誤:", err);
});
這裡 res 是一個 Response 物件(裡面有 status、ok、headers 等等)
fetch 只有在「網路層出問題」時才會 reject,例如:
這種情況才會進 .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」。
答案是 要自己判斷 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);
});
完整 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);
});
有些情況下,錯了也可以「撿回來」繼續走!
當錯誤不是「致命錯誤」,而是「可以用替代方案繼續運作」的時候,就會在 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);
});
舉例:
在首頁載入
使用者資料(重要)、推薦商品(次要)
所以如果推薦商品 API 爆掉,不應該讓整個首頁壞掉。
fetch("/api/recommend")
.catch(err => {
return []; // 推薦商品掛了 → 顯示空清單即可
});
使用者看起來會覺得:「沒推薦商品而已,不影響整體。」
![]()