今天发现错误问题,我已阅读erlang's
错误和错误处理文档。
对于生成的错误类型,有两种类型,一种是exit
,另一种是throw
。
在使用我的源代码后,throw and exit
表达式已经集中。
两者都相似,似乎只是捕捉匹配表达式不同。
(emacs@chen-yumatoMacBook-Pro.local)30> catch throw ({aaa}).
{aaa}
(emacs@chen-yumatoMacBook-Pro.local)31> catch exit ({aaa}).
{'EXIT',{aaa}}
(emacs@chen-yumatoMacBook-Pro.local)32> catch gen_server:call(aaa,{aaa}).
{'EXIT',{noproc,{gen_server,call,[aaa,{aaa}]}}}
你能告诉我什么时候使用throw以及何时使用exit?
答案 0 :(得分:15)
3 类可以使用try ... catch
throw
,error
和exit
来抓住。
throw
是使用throw/1
生成的,旨在用于非本地返回,除非未捕获,否则不会生成错误(当您收到nocatch
错误。
error
。您可以使用error/1
显式生成错误。系统还在生成的错误值中包含堆栈跟踪,例如{badarg,[...]}
。
exit
是使用exit/1
生成的,用于表示此过程将会死亡。
error/1
和exit/1
之间的差异不是很大,更多的是错误产生的堆栈跟踪增强的意图。
在执行catch ...
时,它们之间的差异实际上更明显:当使用throw/1
时,catch
只返回抛出的值,正如非本地返回所期望的那样;当使用error/1
时,catch
会返回{'EXIT',Reason}
,其中Reason
包含堆栈跟踪;来自exit/1
catch
的同时返回{'EXIT',Reason}
,但Reason
仅包含实际退出原因。 try ... catch
看起来等同于他们,但他们是非常不同的。
答案 1 :(得分:6)
[已更新]
我掩盖了罗伯特·维丁指出的抛出和错误之间的重要区别。此编辑仅供记录!
throw error
用于在其他语言中使用throw
的地方。您的代码检测到正在运行的进程中的错误,该错误通过error/1
发出异常信号。相同的进程捕获它(可能在堆栈中更高),并且错误将在同一进程中处理。 error
总是带来一个堆栈跟踪。
throw
用于不发出错误信号,只是用于从深层嵌套函数返回值。
由于它展开堆栈,因此调用throw
会将抛出的值返回到捕获的位置。就像error
的情况一样,我们正在捕获被抛出的东西,只有被抛出的东西不是错误,而只是一个传递给堆栈的值。这就是为什么throw不会带来堆栈跟踪的原因。
作为一个人为的例子,如果我们想要为列表实现exists
函数(类似于list:any
所做的那样)并且在没有的情况下执行练习而不进行递归我们自己,只使用list:foreach
,然后可以使用throw
:
exists(P, List) ->
F = fun(X) ->
case P(X) of
true -> throw(true);
Whatever -> Whatever
end
end,
try lists:foreach(F, List) of
ok -> false
catch
true -> true
end.
抛出但未捕获的值被视为error
:将生成nocatch
异常。
当'放弃'时,EXIT将由进程发出信号。 父 进程处理EXIT,而子进程就死了。这是Erlang let-it-crash理念。
所以exit/1
的EXIT不会被捕获在同一个进程中,而是留给父进程。 error/1
的错误是流程的本地错误 - 即,流程本身发生了什么以及如何处理; throw/1
用于跨堆栈的控制流。
<强> [UPDATE] 强>
exit/2
- 使用 Pid
调用要将EXIT发送到的进程。
exit/1
表示父进程。答案 2 :(得分:0)
我是Erlang的新手,但我想到的是这些东西是什么,它们的区别,它们用于什么等等:
throw
:应在本地处理的条件(即在当前流程中)。例如。调用者正在寻找集合中的元素,但不知道集合是否实际包含这样的元素;然后,如果这样的元素不存在,被调用者可以抛出,并且调用者通过使用try[/of]/catch
来检测缺席。如果调用者忽略了这一点,那么这会变成nocatch
error
(如下所述)。
exit
:当前流程已完成。例如。它已经简单地完成了(在这种情况下,你传递normal
,它被视为与返回的原始函数相同),或者它的操作被取消(例如,它通常无限循环,但刚收到{{ 1}}消息)。
shut_down
:进程已经做了某些事情和/或达到程序员没有考虑的状态(例如1/0),认为是不可能的(例如error
遇到一个值不匹配任何情况),或不满足一些先决条件(例如输入是非空的)。在这种情况下,本地恢复没有意义。因此,case ... of
和throw
都不合适。由于这是意料之外的,因此堆栈跟踪是Reason的一部分。
如您所见,上面的列表按升序排列:
exit
用于调用者应该处理的理智条件。即处理在当前进程中发生。
throw
也是理智的,但应该仅仅因为流程已经完成而结束当前流程。
exit
很疯狂。发生了无法合理恢复的事情(通常是一个错误?),本地恢复不合适。
VS。其他语言:
error
类似于Java中使用的检查异常的方式。然而,throw
的使用方式更类似于未经检查的例外。已检查的异常是您希望调用者处理的异常。 Java要求您在error
中包装调用或声明您的方法try/catch
这样的例外。然而,未经检查的异常通常会传播到最外面的调用者。
throws
在Java,C ++,Python,JavaScript,Ruby等更“常规”的语言中没有很好的类比。exit
模糊地像一个超级 - exit
:而不是最后返回,你可以从一个函数的中间返回,除了你不是从当前函数返回,你从它们返回ALL。
return
示例
exit
由于serve_good_times() ->
receive
{top_of_the_mornin, Sender} ->
Sender ! and_the_rest_of_the_day_to_yourself;
{you_suck, Sender} ->
Sender ! take_a_chill_pill;
% More cases...
shut_down ->
exit(normal)
end,
serve_good_times()
end
在几乎所有消息之后调用自身,程序员已决定我们不希望在每个接收情况下重复该调用。因此,她在接收之后接听了电话。但是,如果serve_good_times
决定停止自我调用怎么办?这是serve_good_times
来救援的地方。将exit
传递给normal
会导致进程终止,就像最后一次函数调用已经返回一样。
因此,在exit
等通用库中调用exit
通常是不合适的。图书馆的业务是否应该结束;这应该由应用程序代码决定。
异常lists
怎么办?
如果另一个进程(“远程”进程)链接到调用exit
(并且未调用exit
)的“本地”进程,则这很重要:就像返回的最后一个函数一样,{ {1}}不会导致远程进程退出。但是,如果本地进程进行process_flag(trap_exit, true)
调用,则远程进程也会以exit(normal)
退出。当然,如果远程进程链接到更多进程,它们也会获得exit(herp_derp)
的退出信号。因此,非Reason=herp_derp
退出会导致连锁反应。
让我们来看看这个:
Reason=herp_derp
我们生成的第一个进程没有导致shell退出(我们可以告诉,因为normal
在1> self().
<0.32.0>
2> spawn_link(fun() -> exit(normal) end).
<0.35.0>
3> self().
<0.32.0>
4>
4>
4> spawn_link(fun() -> exit(abnormal) end).
** exception exit: abnormal
5> self().
<0.39.0>
6>
之前和之后返回了相同的pid)。但是第二个进程确实导致shell退出(并且系统用一个新进程替换了shell进程)。
当然,如果远程进程使用self
,那么它只会收到一条消息,无论本地进程是否将spawn_link
或其他内容传递给process_flag(trap_exit, true)
。设置此标志可以阻止连锁反应。
normal
回想一下,我说exit
被视为原始函数返回:
6> process_flag(trap_exit, true).
false
7> spawn_link(fun() -> exit(normal) end).
<0.43.0>
8> self().
<0.39.0>
9> flush().
Shell got {'EXIT',<0.43.0>,normal}
ok
10>
10>
10> spawn_link(fun() -> exit(abnormal) end).
<0.47.0>
11> self().
<0.39.0>
12> flush().
Shell got {'EXIT',<0.47.0>,abnormal}
你知道什么:同样的事情发生在调用exit(normal)
的时候。精彩!