JavaScript try...catch:錯誤處理與 finally 用法
此語法由 try 區塊所組成,內有一個以上的陳述式。另外,還有 catch 可以捕捉例外的處理。最後,還有 finally 會在 try、catch 之後才執行。它是 流程控制 中專門處理 出錯時該怎麼辦 的語法。
try {
// 執行事項
} catch (err) {
// 出現錯誤的例外事項
} finally {
// 最後才執行的事項
}catch
catch 區塊可以處理所有在 try 區塊裡可能產生的例外。它會收到一個 錯誤物件,常用的屬性有三個:
| 屬性 | 說明 |
|---|---|
name | 錯誤類型,例如:SyntaxError、TypeError |
message | 錯誤訊息,除錯時先看這個 |
stack | 呼叫堆疊,可看出錯誤從哪裡來 |
try {
const data = JSON.parse('這不是 JSON');
console.log(data);
} catch (err) {
// SyntaxError
console.log(err.name);
// 錯誤訊息
console.log(err.message);
}小提醒
若用不到錯誤物件,catch 後面的括號可以整個省略,寫成 catch {}。
finally
finally 會在 try 和 catch 區塊執行以後,才會執行。無論有沒有被拋出的例外,finally 區塊都會執行。如果有被拋出的例外,即使沒有 catch 區塊來處理這些例外,還是會執行 finally 區塊裡的陳述式。
因為它一定會執行,所以最適合放收尾動作,例如:關閉載入動畫:
const elemLoading = document.querySelector('#Loading');
try {
elemLoading.classList.add('is-show');
// 進行可能失敗的工作
} catch (err) {
console.error(err.message);
} finally {
elemLoading.classList.remove('is-show');
}throw 主動拋出錯誤
檢查到不該繼續的狀況時,可以自己拋出錯誤,交給外層的 catch 統一處理:
function getSum(x, y) {
if (typeof x !== 'number' || typeof y !== 'number') {
throw new Error('參數必須是數字');
}
return x + y;
}
try {
console.log(getSum(10, '5'));
} catch (err) {
// 參數必須是數字
console.log(err.message);
}什麼時候該用
適合用在 結果不完全由自己掌控 的地方:
- 解析外部傳來的 JSON。
- 呼叫可能失敗的 Web API 或伺服器。
- 讀寫瀏覽器儲存空間。
注意
try...catch 不是萬用藥。自己寫錯的語法應該修正程式,而不是包一層 catch 把錯誤藏起來;把錯誤吞掉又什麼都不做,只會讓問題更難被發現。至少用 console.error() 留下紀錄。
非同步的陷阱
下面這段的錯誤 接不到,因為回呼是在之後的另一輪才執行:
try {
setTimeout(() => {
throw new Error('接不到');
}, 1000);
} catch (err) {
console.log('這裡不會被執行');
}非同步請在回呼內自行處理,或改用 async 與 await 搭配 try...catch。相關的 定時器 說明可以一併看看。
常見問題
什麼情況需要用 try...catch?
處理結果不完全由自己掌控的程式時,例如:解析外部傳來的 JSON、呼叫可能失敗的 API、讀取瀏覽器儲存空間。單純的語法錯誤是靠修正程式解決,不是靠 catch。
catch 收到的參數是什麼?
是錯誤物件,通常有 name、message 與 stack 三個常用屬性,分別是錯誤類型、錯誤訊息與呼叫堆疊,除錯時先看 message。
finally 一定會執行嗎?
會。無論 try 是否拋出例外、有沒有被 catch 接住,finally 都會執行,因此適合放關閉載入動畫、解除鎖定這類收尾動作。
throw 是做什麼的?
主動拋出錯誤。當檢查到不該繼續的狀況時,可用 throw new Error() 搭配訊息中斷流程,讓外層的 catch 統一處理。
try...catch 能接住非同步的錯誤嗎?
不一定。setTimeout() 回呼裡拋出的錯誤,外層的 try...catch 接不到,因為那段程式已在之後的另一輪執行。非同步請在該回呼內處理,或用 async 與 await 搭配 try...catch。