fetch 逾時怎麼處理:用 AbortSignal.timeout() 取消慢請求
fetch() 卡住時,很多人會寫一個 Promise.race(),但留下的請求仍可能在背景執行。現在瀏覽器提供 AbortSignal.timeout():它不只讓等待逾時,還能把 signal 傳給 fetch,要求請求中止。
直接做法是:把 timeout signal 傳進 fetch,並把逾時、使用者取消、真正的網路或 HTTP 錯誤分開處理。 逾時不是「服務一定壞了」,而是這次呼叫超過你的介面願意等待的時間。
最小 timeout 範例
async function loadProfile() { try { const response = await fetch('/api/profile', { signal: AbortSignal.timeout(8_000), });
if (!response.ok) { throw new Error(`HTTP ${response.status}`); }
return await response.json(); } catch (error) { if (error.name === 'TimeoutError') { throw new Error('讀取逾時,請稍後再試'); } throw error; }}fetch 在 HTTP 404 或 500 時不會自行 reject,因此仍要看 response.ok。相反地,signal 被中止時,等待中的 fetch 會 reject;錯誤名稱要依中止原因判斷,不要一律顯示「網路斷線」。
逾時與使用者取消不是同一件事
搜尋建議、切換路由或離開 modal 時,較好的做法是保留一個 AbortController,讓新操作主動取消舊請求:
let activeController;
async function search(query) { activeController?.abort(); activeController = new AbortController();
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`, { signal: activeController.signal, });
if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json();}這裡的取消是 UI 狀態更新,不該彈出錯誤通知。若同時需要使用者取消與逾時,可用 AbortSignal.any() 合併 signal;捕捉例外後,檢查是哪個 signal 已 aborted,再決定訊息與重試策略。
不要用 timeout 掩蓋後端問題
timeout 值應從操作目的決定:輸入搜尋可短,儲存草稿或匯出檔案應讓使用者看見進度並提供取消。若同一 endpoint 經常超時,請記錄 request ID、URL、時間與回應狀態,回到 API、資料庫或代理層找原因。
而且中止 client 端等待不保證伺服器已停止工作。對有副作用的 POST、付款或建立資源操作,伺服器端仍要有 idempotency key 或可查詢的處理狀態;不要因為前端超時就盲目重送。
這類取消邏輯適合放在讀取遠端資料的元件邊界;若你還要處理 cookie、credentials 或 CORS,可先看 Fetch API 與 Cookie 的設定方式。
檢查清單
- 對 HTTP status 明確判斷,不把
fetchresolve 當成成功。 - 對 timeout、手動 abort 與其他錯誤給不同處理。
- 元件卸載或查詢條件改變時,取消不再需要的讀取。
- 有副作用的請求由伺服器負責冪等與最終狀態,前端 timeout 不當作失敗證明。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。