Nodejs exception handling
例外處理(Error handling or exception handling) 永遠都是程式設計師的惡夢,我曾是(現在也是 XD)一個 java 程式設計師, java 內的 try catch 也是挺惱人的,即便 java 7 裡面有新的 muti try catch ,但是還是挺麻煩的。
轉到 nodejs 之後,發現 javascript 的 error handling 也不遑多讓,因為 nodejs 充斥了大量的 async ,所以處理起來格外辛苦,不過基本上來說,還是有一些準則可以來參考。
接著,讓我們話說重頭,從 sync code 開始來探討
先看以下的 code
{% include_code blog-ianwu-tw-example-master/20130715_error_handling/syncCall_1.js %}
這隻是典型的 sync call ,我們呼叫了 syncCall()
預期回傳一個字串,但是這個 function 可能會發生 error。
執行一下,果然出了 exception
onlinemad:~ > ~/20130715_error_handling > node syncCall_1.js
/20130715_error_handling/syncCall_1.js:2
throw new Error('sync exception');
^
Error: sync exception
at syncCall (/20130715_error_handling/syncCall_1.js:2:9)
at Object.<anonymous> (/20130715_error_handling/syncCall_1.js:6:1)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:901:3
設計這個 function 的人採用了 throw 把 error 丟出去給呼叫 syncCall()
的人處理,所以當使用 syncCall()
的時候必須注意 exception 的處理。
接著看下一個範例
{% include_code blog-ianwu-tw-example-master/20130715_error_handling/syncCall_2.js %}
在 sync 的狀況下,用 try catch 就可以把 error 給抓住,所以 code 增加 try catch 來捕捉 exception
執行一下,果不其然 exception 被抓住並印出
onlinemad:~ > ~/20130715_error_handling > node syncCall_2.js
catch sycn exception
以上是 sync code 對於 exception 的處理方式。
不過正如一開始所說的 nodejs 充斥了大量的 async call 所以在處理 async call 的 exception 方式就跟 sync call 有很大的不同
先看下面這段 code
{% include_code blog-ianwu-tw-example-master/20130715_error_handling/asyncCall_1.js %}
這隻是典型的 async call ,我們呼叫了 asyncCall()
傳入一個 callback function ,asyncCall()
做完後會把字串透過 callback function 傳回來,但是這個 async function 也可能會發生 error。
於使我們用 try catch 來捕捉 exception 看看
onlinemad:~/20130715_error_handling > node asyncCall_1.js
/20130715_error_handling/asyncCall_1.js:3
throw new Error('async exception');
^
Error: async exception
at /20130715_error_handling/asyncCall_1.js:3:9
at process._tickCallback (node.js:415:13)
at Function.Module.runMain (module.js:499:11)
at startup (node.js:119:16)
at node.js:901:3
很遺憾,在 async 的狀況下,無法使用 try catch 的方式來捕捉 exception。
所以在 async 的狀況下我們就要將 exception 透過 callback function 傳給 caller 來處理。
{% include_code blog-ianwu-tw-example-master/20130715_error_handling/asyncCall_2.js %}
接著,我們修改 code ,把 exception 傳回給 caller,所以 callback function 的第一個參數就是 exception,在 caller 這邊的就要檢查 err
的狀態,如果 not null 就表示出 exception 了。
執行一下,果然可以抓住 async exception
onlinemad:~/20130715_error_handling > node asyncCall_2.js
catch asycn exception
回想 asyncCall_1.js
這個範例 asyncCall()
做完後會把字串透過 callback function 傳回來
所以回傳的字串呢?
再合併 asyncCall_2.js
這個範例
把 exception 傳回給 caller,所以 callback function 的第一個參數就是 exception
所以 async call exception handling 最佳作法如下
{% include_code blog-ianwu-tw-example-master/20130715_error_handling/asyncCall_3.js %}
當有 exception 發生時把 exception 放在 callback function 的第一個參數傳回給 caller,當一切正常時 callback function 的第一個參數就擺 null 第二個參數擺執行的結果,這樣一來就可以完美地處理 async call 所發生的 exception 。
執行一下,當沒有 exception 發生時,就會把字串傳回給 callback function;當發生 exception 時就可以抓住 exception。
onlinemad:~/20130715_error_handling > node asyncCall_3.js
catch asycn exception
onlinemad:~/20130715_error_handling > node asyncCall_3.js
asyncCall
除了傳 error 給 callback function 的方式之外,還可以用 EventEmitter trigger 特定的事件。
將 asyncCall_3.js
的範例修改如下
{% include_code blog-ianwu-tw-example-master/20130715_error_handling/asyncCall_catch_by_event.js %}
當有 exception 發生時 emit 一個 error 事件,接著再把 exception 傳回去;當一切正常時 emit 一個 success 事件,同時把字串傳回去。
接著在使用這個 class 的時候就必須使用 on
綁一個 function 上去,這樣 emit 的事件才有對應的 function 可以處理。
執行一下,基本上跟 asyncCall_3.js
的範例一樣。
onlinemad:~/20130715_error_handling > node asyncCall_catch_by_event.js
[Error: async exception]
onlinemad:~/20130715_error_handling > node asyncCall_catch_by_event.js
asyncCall
另外還有兩種方式可以 handle exception,分別是使用 domain 跟 catch uncaughtException
這個範例是使用 domain 來 handle exception
{% include_code blog-ianwu-tw-example-master/20130715_error_handling/asyncCall_catch_by_domain.js %}
用 d.run
把可能會出 exception 的 function 包起來,所以
執行一下,不論是 sync 或是 async call 都可以被 domain 的 on error function 抓住並處理
onlinemad:~/20130715_error_handling > node asyncCall_catch_by_domain.js
catch sync exception
catch async exception
不過 domain 的機制並不是再取代 try catch 也不是去抓住 async 的 exception,之後會在針對 domain 這個 module 來作探討。
更多細節可以參考 Node.js 的 API: Domain Node.js v0.10.15 Manual & Documentation
最後一種方式就是 catch uncaughtException 在 process module 可以綁一個 uncaughtException 事件
這個範例是使用 uncaughtException 事件來 handle exception
{% include_code blog-ianwu-tw-example-master/20130715_error_handling/asyncCall_catch_by_uncaughtException.js %}
執行一下,錯誤可以被 uncaughtException 的事件抓住並處理
onlinemad:~/20130715_error_handling > node asyncCall_catch_by_uncaughtException.js
catch async exception
不過不建議 uncaughtException 來處理 exception,因為官方有說
Note that uncaughtException is a very crude mechanism for exception handling and may be removed in the future.
所以寫出來只是讓大家看看而已...
語言能做的就是提供好的方式去抓 exception,但是抓到 exception 後要怎樣處理那就是 programmer 要決定的事情,是要停止執行還是繼續執行替代 function 這就不在語言的範圍了。
總結 Node.js 的 exception handling 以 syncCall_2.js
及 asyncCall_3.js
的方式去 handle exception 會是最好的方式。
ref
- Node.js Best Practice Exception Handling - Stack Overflow
- Machado Gustavo's Blog
- Node.js error handling
所有的範例都放在 github githut:onlinemad/blog-ianwu-tw-example 有興趣的朋友可以 clone 一份回家 :)