Error handling, "try..catch"

程式執行會發生很多錯誤,原因千奇百怪,錯誤會讓程式停止執行,我們可以用 try..catch 讓程式除了停止執行外,可以對錯誤進行處理。

The “try…catch” syntax

// syntax
try {

  // code...

} catch (err) {

  // error handling

}

// 沒有錯誤發生
try {

  alert('Start of try runs');  // (1) <--

  // ...no errors here

  alert('End of try runs');   // (2) <--

} catch(err) {

  alert('Catch is ignored, because there are no errors'); // (3)

}

alert("...Then the execution continues");

// 錯誤發生
try {

  alert('Start of try runs');  // (1) <--

  lalala; // error, variable is not defined!

  alert('End of try (never reached)');  // (2)

} catch(err) {

  alert(`Error has occurred!`); // (3) <--

}

alert("...Then the execution continues");

// try..catch 能夠運作的前提是程式碼是有效的程式碼,因為引擎讀不懂程式碼,所以不會執行 try..catch
try {
  {{{{{{{{{{{{
} catch(e) {
  alert("The engine can't understand this code, it's invalid");
}

// 如果有非同步的程式碼,因為已經執行完 try..catch,所以對非同步的 setTimeout 裡的程式碼
// 不會捕捉錯誤
try {
  setTimeout(function() {
    noSuchVariable; // script will die here
  }, 1000);
} catch (e) {
  alert( "won't work" );
}

// 要捕捉 setTimeout 裡程式碼的錯誤,要在裡面加上 try..catch
setTimeout(function() {
  try {
    noSuchVariable; // try..catch handles the error!
  } catch {
    alert( "error is caught here!" );
  }
}, 1000);

Error object

// 捕捉到的錯誤,會返回作為 catch 的參數
try {
  // ...
} catch(err) { // <-- the "error object", could use another word instead of err
  // ...
}

// 錯誤物件包含 name message 2 個屬性,還有很多非標準的屬性,最多被廣泛運用的是 stack
try {
  lalala; // error, variable is not defined!
} catch(err) {
  alert(err.name); // ReferenceError
  alert(err.message); // lalala is not defined
  alert(err.stack); // ReferenceError: lalala is not defined at ...

  // Can also show an error as a whole
  // The error is converted to string as "name: message"
  alert(err); // ReferenceError: lalala is not defined
}

Optional “catch” binding

// 最近新增的如果不需要錯誤訊息,catch 可以忽略它
try {
  // ...
} catch {
  // error object omitted
}

Using “try…catch”

// 實際使用 try..catch 情形
let json = '{"name":"John", "age": 30}'; // data from the server

let user = JSON.parse(json); // convert the text representation to JS object

// now user is an object with properties from the string
alert( user.name ); // John
alert( user.age );  // 30let json = "{ bad json }";

// 用 try..catch 處理錯誤,我們只印出錯誤訊息,但可以做其他處理,
// 发送一个新的网络请求,给用户提供另外的选择,把异常信息发送给记录日志的工具
try {

  let user = JSON.parse(json); // <-- 当这里抛出异常...
  alert( user.name ); // 不工作

} catch (e) {
  // ...跳到这里继续执行
  alert( "Our apologies, the data has errors, we'll try to request it one more time." );
  alert( e.name );
  alert( e.message );
}

Throwing our own errors

// 程式碼運行正常,但沒有我們要的 name 屬性。
let json = '{ "age": 30 }'; // incomplete data

try {

  let user = JSON.parse(json); // <-- no errors
  alert( user.name ); // no name!

} catch (e) {
  alert( "doesn't execute" );
}

// 可以用 throw 自定義錯誤
throw <error object>

// syntax,有內建的錯誤建構函式 Error、 SyntaxError、ReferenceError、TypeError 等等
let error = new Error(message);
// or
let error = new SyntaxError(message);
let error = new ReferenceError(message);
// ...

// example,name 屬性是建構函式的名稱,message 屬性來自丟入的參數
let error = new Error("Things happen o_O");

alert(error.name); // Error
alert(error.message); // Things happen o_O

try {
  JSON.parse("{ bad json o_O }");
} catch(e) {
  alert(e.name); // SyntaxError
  alert(e.message); // Unexpected token o in JSON at position 0
}

// 實際使用
let json = '{ "age": 30 }'; // incomplete data

try {

  let user = JSON.parse(json); // <-- no errors

  if (!user.name) {
    throw new SyntaxError("Incomplete data: no name"); // (*)
  }

  alert( user.name );

} catch(e) {
  alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name
}

Rethrowing

// 有可能在 try 發生預料之外的異常,這裡拋出 JSON Error 的錯誤是不對的錯誤訊息
let json = '{ "age": 30 }'; // incomplete data

try {
  user = JSON.parse(json); // <-- forgot to put "let" before user

  // ...
} catch(err) {
  alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined
  // (no JSON Error actually)
}

// 我們可以通過其他方式知道正確的錯誤訊息
try {
  user = { /*...*/ };
} catch(e) {
  alert(e.name); // "ReferenceError" for accessing an undefined variable
}

// catch 捕捉已知的錯誤,拋出其他未知的錯誤
// rethrowing 的規則,捕捉所有錯誤,catch(err) {...} 處理已知錯誤,未知錯誤 throw err
let json = '{ "age": 30 }'; // 不完整的数据
try {

  let user = JSON.parse(json);

  if (!user.name) {
    throw new SyntaxError("Incomplete data: no name");
  }

  blabla(); // 预料之外的异常

  alert( user.name );

} catch(e) {

  if (e.name == "SyntaxError") {
    alert( "JSON Error: " + e.message );
  } else {
    throw e; // rethrow (*)
  }

}

// 未知的錯誤會被外層 try..catch 處理
function readData() {
  let json = '{ "age": 30 }';

  try {
    // ...
    blabla(); // 异常!
  } catch (e) {
    // ...
    if (e.name != 'SyntaxError') {
      throw e; //  重新抛出(不知道如何处理它)
    }
  }
}

try {
  readData();
} catch (e) {
  alert( "External catch got: " + e ); // 捕获到!
}

try…catch…finally

// syntax,不果有沒有錯誤發生 finally 都會執行
try {
   ... try to execute the code ...
} catch(e) {
   ... handle errors ...
} finally {
   ... execute always ...
}

// If you answer “Yes”, then try -> catch -> finally.
// If you say “No”, then try -> finally.
try {
  alert( 'try' );
  if (confirm('Make an error?')) BAD_CODE();
} catch (e) {
  alert( 'catch' );
} finally {
  alert( 'finally' );
}

// 不論有沒有結果都會印出計算時間
let num = +prompt("Enter a positive integer number?", 35)

let diff, result;

function fib(n) {
  if (n < 0 || Math.trunc(n) != n) {
    throw new Error("Must not be negative, and also an integer.");
  }
  return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}

let start = Date.now();

try {
  result = fib(num);
} catch (e) {
  result = 0;
} finally {
  diff = Date.now() - start;
}

alert(result || "error occured");

alert( `execution took ${diff}ms` );

// try..catch 結束執行包含 return,finally 會先被執行,才會執行外部程式碼。
function func() {

  try {
    return 1;

  } catch (e) {
    /* ... */
  } finally {
    alert( 'finally' );
  }
}

alert( func() ); // first works alert from finally, and then this one

// 如果暫時不想處理錯誤,用 try..finally 確保程式執行完畢
function func() {
  // 开始做需要被完成的操作(比如测量)
  try {
    // ...
  } finally {
    // 完成前面要做的事情,即使 try 里面执行失败
  }
}

Global catch

// try..catch 之外的程式碼,出現了嚴重錯誤,在瀏覽器環境可以使用 window.onerror 處理
window.onerror = function(message, url, line, col, error) {
  // ...
};

// example,不是去處理所有錯誤,而是提供開發者錯誤訊息
<script>
  window.onerror = function(message, url, line, col, error) {
    alert(`${message}\n At ${line}:${col} of ${url}`);
  };

  function readData() {
    badFunc(); // 哦,出问题了!
  }

  readData();
</script>

Last updated