[Erlang37]error/1 exit/1 exit/2 throw/1的区别
1. error/1
主要是系统用来定义内部错误的: Erlang内建的run time error 一共有10种:
function_clause/case_clause/if_clause/badmatch/badarg/undef/badarith/badfun/badarity/system_limit, 比如:
1> erlang:binary_to_list(1). ** exception error: bad argument in function binary_to_list/1 called as binary_to_list(1)
这上面就是触发了error/1,我们也可以手动触发一下。
2> erlang:error(badarg). ** exception error: bad argument
注意到erlang直接把badarg这种内建的error转成更详细的bad argument,
更进一步,我们也可以使用error/1定义自己的错误
3> erlang:error("this is my own error").
** exception error: "this is my own error"
这一次,自定义的错就没有被erlang shell认出来。
2. exit/1 exit/2
exit有internal exits 和 external exits的区别,我们可以使用exit(Pid,Reason)让别一个进程退出。
exit/1和error/1非常相似,很多时候可以通用,便是exit语境是退出,更适合于进程退出的情况,还有一个区别就是
exit/1不会带调用的stack trace信息(方便让其它进程退出时不用带非常大的调用信息,更轻量)。但是error/1会带。
4> catch exit(test). {'EXIT',test} 5> catch error(test). {'EXIT',{test,[{erl_eval,do_apply,6, [{file,"erl_eval.erl"},{line,674}]}, {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,431}]}, {shell,exprs,7,[{file,"shell.erl"},{line,686}]}, {shell,eval_exprs,7,[{file,"shell.erl"},{line,641}]}, {shell,eval_loop,3,[{file,"shell.erl"},{line,626}]}]}}
3. throw/1
throw/1 它最常用配合 try...of catch 处理嵌套case(可以快速跳出),它所携带的信息最少(比exit/1还少一个'EXIT'):
6> catch throw(2+2). 4 7> catch 2+2 4
上面2个case用的catch,都区分不出结果是throw出来的,还是正常计算得到的结果,所以这也是推荐使用try .. of catch的原因:
8> try throw(2+2) of 8> V -> {ok, V} 8> catch 8> throw:V -> {error, V} 8> end. {error,4}
4. 总结:
进程退出使用exit/1或exit/2, 想快速跳出recursion或快速跳回Top-Level函数时用throw/1,尽量不要使用error/1,
如果需要得到调用的stack trace信息,可以自己显式的调用erlang:get_stacktrace().得到当前进程最新一次Exception时的的stacktrace。
5. 扩展(gen_server中的几种退出进程方法比较)
5.1. 在init不成功时返回{stop, Reason} 退出; 5.2. 处理消息出错,或正常退出时返回{stop, Reason, NewState}); 5.3. 使用exit直接退出进程; 5.4. 使用throw退出进程; 5.5. 使用error退出进程(由于我们前面讲了error主要用于定义内部错误,所以不推荐使用)。
try_dispatch(Mod, Func, Msg, State) -> try {ok, Mod:Func(Msg, State)} catch throw:R -> {ok, R}; error:R -> Stacktrace = erlang:get_stacktrace(), {'EXIT', {R, Stacktrace}, {R, Stacktrace}}; exit:R -> Stacktrace = erlang:get_stacktrace(), {'EXIT', R, {R, Stacktrace}} end.
使用stop就是正常退出,不带stack trace信息(他本来就没有crash,也没有stack信息)
使用exit退出,它带了stack trace信息。
{noreply, NewState}
{reply, ok, NewState}
{stop, normal, NewState}
Notice that for any other reason than normal, shutdown, or {shutdown,Term}, the gen_server process is assumed to terminate because of an error and an error report is issued using error_logger:format/2.
=ERROR REPORT==== 31-Oct-2016::15:10:44 === ** Generic server test_throw_exit terminating ** Last message in was go ** When Server state == {state} ** Reason for termination == ** not_shutdown_term
所以,如果我们在正常退出或shutdown时不需要那一列烦人的log,就把reason定义为三者中的一种就行了。
具体源码可见: https://github.com/erlang/otp/blob/maint/lib/stdlib/src/gen_server.erl#L809
6. 参考资料:
Erlang官方文档:http://erlang.org/doc/reference_manual/errors.html
Learnyousomeerlang关于Exceptions的介绍: http://learnyousomeerlang.com/errors-and-exceptions