🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
前言
現在寫異步代碼,基本上就是 Promise 和 async/await 兩種寫法。雖然這倆都能幹同樣的活,但 async/await 寫起來更像同步代碼,讀起來更順眼,所以很多人都説它是"異步編程的終極方案"。
不過有個問題挺讓人糾結的:用 async/await 的時候,到底要不要加 try catch 來抓錯誤?我看了好多項目代碼,發現大家做法都不一樣,有的加有的不加,甚至有的連 catch 都不寫,這就很迷了。
先看幾種寫法
寫法1:老實用 try catch
function getUserInfo () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('請求出錯了哦')
}, 1000)
})
}
async function logined () {
try {
let userInfo = await getUserInfo()
// 如果上面出錯了,這裏就不會執行了
let pageInfo = await getPageInfo(userInfo?.userId)
} catch(e) {
console.warn('抓到一個錯誤:', e)
}
}
logined()
這種寫法的好處是錯誤能抓住,而且代碼會在出錯的地方停下來,不會繼續往下執行。缺點就是 try catch 佔了太多行數,每個接口都這麼寫的話,代碼看起來有點囉嗦。
寫法2:直接在後面接 .catch()
function getUserInfo () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('請求出錯了哦')
}, 1000)
})
}
async function logined () {
let userInfo = await getUserInfo().catch(e => console.warn('抓到一個錯誤:', e))
// 注意:這裏即使出錯了也會繼續執行!
if (!userInfo) return // 所以得手動檢查一下
let pageInfo = await getPageInfo(userInfo?.userId)
}
logined()
這種寫法也能抓住錯誤,但是程序不會自動停下來,還會繼續往下走。所以你得自己檢查 userInfo 是不是空的,不然可能會出問題。
寫法3:在 catch 裏再 reject
function getUserInfo () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('請求出錯了哦')
}, 1000)
})
}
async function logined () {
let userInfo = await getUserInfo().catch(e => {
console.warn('抓到一個錯誤:', e)
return Promise.reject(e) // 這裏再扔出去一次
})
// 這樣就會停在這裏了
let pageInfo = await getPageInfo(userInfo?.userId)
}
logined()
這種寫法能讓程序在出錯的地方停下來,但是會在控制枱留下一個 "uncaught (in promise)" 的錯誤提示,有些人覺得這樣不太好看。
到底該用哪種?
其實在真實項目裏,我們一般會用 axios 或 fetch 這些庫來發請求,通常會對它們進行一層封裝。在封裝的時候就可以統一處理錯誤了。
至於要不要在 await 後面加 try catch,主要看你想不想讓程序在出錯的時候停下來:
-
不想中斷程序,可以這樣寫:
let userInfo = await getUserInfo().catch(e => console.warn(e)) if (!userInfo) return // 記得檢查一下是不是空的
這樣控制枱不會報紅字錯誤。
- 想中斷程序,又不想看控制枱報錯,就用 try catch:
try {
let userInfo = await getUserInfo()
let pageInfo = await getPageInfo(userInfo?.userId)
} catch(e) {
console.warn(e)
}
- 想中斷程序,又不介意控制枱報錯:
let userInfo = await getUserInfo().catch(e => {
console.warn(e)
return Promise.reject(e)
})
// 這裏會停下來
let pageInfo = await getPageInfo(userInfo?.userId)
我的建議
從我個人的使用體驗來看,try catch 是最好的選擇:
- 代碼邏輯更清晰,更像同步代碼的寫法
- 錯誤處理集中在一個地方,好管理
- 控制枱不會出現一堆難看的 "uncaught (in promise)" 錯誤
- 符合直覺:出錯了就應該停下來
雖然 try catch 會讓代碼多幾行,但是比起代碼的可讀性和可維護性,這點代價是值得的。
既然都已經用 async/await 來寫同步風格的代碼了,那就乾脆用到底,用 try catch 來處理錯誤,這樣代碼風格更統一,讀起來也更舒服。