Skip to content

JavaScript try...catch:錯誤處理與 finally 用法

JavaScript try...catch:錯誤處理

此語法由 try 區塊所組成,內有一個以上的陳述式。另外,還有 catch 可以捕捉例外的處理。最後,還有 finally 會在 trycatch 之後才執行。它是 流程控制 中專門處理 出錯時該怎麼辦 的語法。

js
try {
  // 執行事項
} catch (err) {
  // 出現錯誤的例外事項
} finally {
  // 最後才執行的事項
}

catch

catch 區塊可以處理所有在 try 區塊裡可能產生的例外。它會收到一個 錯誤物件,常用的屬性有三個:

屬性說明
name錯誤類型,例如:SyntaxErrorTypeError
message錯誤訊息,除錯時先看這個
stack呼叫堆疊,可看出錯誤從哪裡來
js
try {
  const data = JSON.parse('這不是 JSON');
  console.log(data);
} catch (err) {
  // SyntaxError
  console.log(err.name);
  // 錯誤訊息
  console.log(err.message);
}

小提醒

若用不到錯誤物件,catch 後面的括號可以整個省略,寫成 catch {}

finally

finally 會在 trycatch 區塊執行以後,才會執行。無論有沒有被拋出的例外,finally 區塊都會執行。如果有被拋出的例外,即使沒有 catch 區塊來處理這些例外,還是會執行 finally 區塊裡的陳述式。

因為它一定會執行,所以最適合放收尾動作,例如:關閉載入動畫:

js
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 統一處理:

js
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() 留下紀錄。

非同步的陷阱

下面這段的錯誤 接不到,因為回呼是在之後的另一輪才執行:

js
try {
  setTimeout(() => {
    throw new Error('接不到');
  }, 1000);
} catch (err) {
  console.log('這裡不會被執行');
}

非同步請在回呼內自行處理,或改用 asyncawait 搭配 try...catch。相關的 定時器 說明可以一併看看。

常見問題

什麼情況需要用 try...catch?

處理結果不完全由自己掌控的程式時,例如:解析外部傳來的 JSON、呼叫可能失敗的 API、讀取瀏覽器儲存空間。單純的語法錯誤是靠修正程式解決,不是靠 catch

catch 收到的參數是什麼?

是錯誤物件,通常有 namemessagestack 三個常用屬性,分別是錯誤類型、錯誤訊息與呼叫堆疊,除錯時先看 message

finally 一定會執行嗎?

會。無論 try 是否拋出例外、有沒有被 catch 接住,finally 都會執行,因此適合放關閉載入動畫、解除鎖定這類收尾動作。

throw 是做什麼的?

主動拋出錯誤。當檢查到不該繼續的狀況時,可用 throw new Error() 搭配訊息中斷流程,讓外層的 catch 統一處理。

try...catch 能接住非同步的錯誤嗎?

不一定。setTimeout() 回呼裡拋出的錯誤,外層的 try...catch 接不到,因為那段程式已在之後的另一輪執行。非同步請在該回呼內處理,或用 asyncawait 搭配 try...catch

延伸閱讀