再見 try/catch,我有更好的選擇了!
前端異步代碼里的錯誤處理,大多數人第一反應還是 try/catch。它能解決問題,但寫多了難免覺得啰嗦:嵌套多、邏輯被打斷,讀起來也不太舒服。
所以這幾年,越來越多項目開始嘗試別的方式——寫法更輕,結構更清晰。
這篇文章就來聊聊三種替代方案:一個語言層的提案,一個可以自己封裝的小工具,還有一個現成的社區庫。選好用的方式,能讓錯誤處理簡單不少。

傳統 try/catch 的痛點
寫 JavaScript 的人都知道,try/catch 雖然是處理異步錯誤的“正道”,但一旦多起來,整個代碼就開始變得臃腫、重復、難讀。
你可能見過這種寫法:
try {
const data = await fetchUser();
doSomething(data);
} catch (e) {
console.error('出錯了', e);
}寫一兩個還好,但如果你有十幾個異步調用,每個都要包一層 try/catch,不僅煩,而且破壞代碼結構。不少人甚至為了偷懶,直接不處理錯誤或者一把包住:
try {
// 一大堆 await
} catch (e) {
// 一個錯誤搞不清是哪里來的
}有沒有更好的寫法?有,而且不止一種。
語言層面的嘗試:try 操作符提案
一個值得關注的思路來自一個全新的語言提案,它設想在 JavaScript 中引入一種新的 try 表達式語法,它不是語句,而是一個表達式。
提案地址:https://github.com/arthurfiorette/proposal-try-operator
const [ok, err, result] = try await fetchUser();這個寫法的意思很明確:
- 如果成功,ok 是 true,result 有值;
- 如果失敗,ok 是 false,err 是錯誤對象。
這樣一來,不僅避免了冗長的 try/catch,還天然具備結構化的錯誤處理方式。
const [ok, err, user] = await safeAwait(fetchUser());
if (!ok) {
console.error('請求失敗:', err);
return;
}
console.log('用戶數據:', user);是不是很像 Go 的 val, err := fn(),或者 Rust 的 Result?這就是提案的核心:讓錯誤處理從控制流轉向值表達式。
雖然這個提案還在 Stage 1,離真正進入 JavaScript 還有一段距離,但它提出了一種很有前景的思路:
錯誤不一定要“捕獲”,也可以像值一樣被“解構”。
自定義封裝:手寫一個 safeAwait
語言層還沒進化?那我們就自己造個輪子。
一個常見的思路是:將 Promise 的執行結果封裝成一個三元組 [ok, err, data],結構明確,邏輯清晰。來看實現:
export type SafeAwaitResult<T> =
| [true, null, T]
| [false, Error, null];
exportasyncfunction safeAwait<T>(promise: Promise<T>): Promise<SafeAwaitResult<T>> {
try {
const result = await promise;
return [true, null, result];
} catch (err: any) {
const error = err instanceofError ? err : newError(String(err));
return [false, error, null];
}
}使用時非常直觀:
const [ok, err, user] = await safeAwait(fetchUser());
if (!ok) {
console.error('請求失敗:', err);
return;
}
console.log('用戶數據:', user);這套封裝的好處是顯而易見的:
- 語義清晰:ok 表示狀態,err 和 data 結構穩定
- 無 try/catch:邏輯更線性,閱讀友好
- 類型明確:配合泛型推導,IDE 提示清晰
- 易于復用:在整個項目中統一處理異步異常
而且你還可以鏈式使用,避免回到嵌套地獄:
const [ok1, err1, user] = await safeAwait(fetchUser());
if (!ok1) return handle(err1);
const [ok2, err2, posts] = await safeAwait(fetchPosts(user.id));
if (!ok2) return handle(err2);
renderDashboard(user, posts);這種寫法非常適合搭配中間件、hooks 或服務層封裝,逐漸成為許多項目的標準做法。
用庫更香:await-to-js 一步到位
如果你不想自己封裝,還有一個現成、穩定的庫可以用:await-to-js

它的設計初衷和 safeAwait 類似,把 Promise 的結果轉成 [error, result] 形式:
npm install await-to-js使用方法如下:
import to from 'await-to-js';
const [err, data] = await to(fetchUser());
if (err) return handle(err);
render(data);如果你的項目希望快速接入結構化的錯誤處理,不妨試試這個庫。
總結對比:三種錯誤處理方案
方法 | 優點 | 缺點 | 適合場景 |
try/catch | 原生支持,語義明確 | 冗長、嵌套、難組合 | 控制分支復雜的邏輯 |
safeAwait (自定義) | 簡潔清晰,類型安全,可組合 | 需要維護封裝結構 | 中大型項目,統一風格 |
await-to-js (第三方庫) | 即裝即用,社區成熟 | 多一個依賴 | 快速落地,團隊協作 |
函數式時代的錯誤處理該進化了
在今天,繼續用 try/catch 處理每一個異步錯誤,已經有些過時。無論是語言層面的提案,還是我們可以自己實現的封裝,甚至是社區提供的優秀工具,目的都是一樣的:讓錯誤處理變得更清晰、更優雅、更現代。
再見了,重復的 try/catch,寫更清爽的代碼,從現在開始。






























