我在Dart写了一个网络服务器并且有关于例外的问题。在我的HttpServer requesthandler中,我在整个方法中添加了一个try-catch块:
try{
...
} catch(e) {
...
}
所以我希望这会阻止任何客户端请求崩溃网络服务器。问题是,当从这个块中抛出某些异常(大量嵌套在其他模块中,但仍然从该块启动)时,可以崩溃。以下是此类例外的示例:
Unhandled exception:
FutureUnhandledException: exception while executing Future
Illegal argument(s)
original stack trace:
#0 _StringBase._createFromCodePoints (dart:core-patch:1403:3)
#1 _StringBase.createFromCharCodes (dart:core-patch:1400:33)
#2 String.String.fromCharCodes (dart:core-patch:1788:43)
#3 _StringDecoderBase.decoded (dart:io:6485:12)
#4 _File.readAsString.<anonymous closure> (dart:io:1307:29)
#5 _FutureImpl.transform.<anonymous closure> (bootstrap:881:37)
#0 _FutureImpl._complete (bootstrap:844:11)
#1 _FutureImpl._complete (bootstrap:848:5)
#2 _FutureImpl._setException (bootstrap:873:14)
#3 _CompleterImpl.completeException (bootstrap:948:30)
#4 _FutureImpl.transform.<anonymous closure> (bootstrap:884:36)
#5 _FutureImpl._complete (bootstrap:840:19)
#6 _FutureImpl._complete (bootstrap:848:5)
#7 _FutureImpl._setValue (bootstrap:862:14)
#8 _CompleterImpl.complete (bootstrap:945:26)
#9 _File.readAsBytes.<anonymous closure> (dart:io:1281:25)
#10 _BaseDataInputStream._checkScheduleCallbacks.issueCloseCallback (dart:io:6345:59)
#11 _Timer._createTimerHandler._handleTimeout (dart:io:6918:28)
#12 _Timer._createTimerHandler._handleTimeout (dart:io:6926:7)
#13 _Timer._createTimerHandler.<anonymous closure> (dart:io:6934:23)
#14 _ReceivePortImpl._handleMessage (dart:isolate-patch:37:92)
为什么这不会在try-catch块中被捕获?它被抛入从其中调用的代码中(即使它没有显示在堆栈跟踪中)。
我希望我错过了关于Dart中异常如何工作的内容,所以我希望你能开导我:)
答案 0 :(得分:5)
使用Future,您必须使用catchError方法来处理异常。
答案 1 :(得分:3)
一旦你意识到发生了什么,理解这个问题非常简单。
问题是:传统控制流结构(if
,while
,try/catch/finally
,return
)的语义纯粹是同步。他们希望程序像源代码流一样流动。看看这个:
1 try {
2 while (...) {
3 if (...) {
4 doSomething();
5 doSomethingElse();
6 }
7 }
8 } catch (e) {
9 print('oh no, something wrong happen! error: $e');
10 } finally {
11 print('done!');
12 }
该程序作为序列工作。第1行在第3行之前执行第2行。在第4行之后立即执行第5行。在第7行之后执行第11行,如果发生异常,第11行也在第9行之后执行。这就是什么同步表示。
然而,同步程序不再足够好。事件处理自然是异步,您可以在任何地方找到事件 - 从用户界面到高度可扩展的网络服务器。所以,如果你写
1 try {
2 var text = 'this will be replaced by the content of the file';
3 new File('...').readAsText().then((result) {
4 text = result;
5 doSomethingThatMightCauseAnException(text);
6 print('read file, got $text');
7 });
8 print('invoked file read');
9 return text;
10 } catch (e) {
11 print('error: $e');
12 }
您必须了解您正在调用异步操作(readAsText
方法)。在这个程序中,第2行在第1行之后执行,第3行在第2行之后执行,但第4行在第3行之后不执行。文件读取是异步调用的(想想“在后台”)并继续该计划。因此,在这种情况下,在第3行之后,您将直接进入第8行。因此,第9行(第8行之后)的return
语句始终返回'this will be replaced by the content of the file'
文本。
然后,程序继续,直到完成(它退出main
功能)。但它不会停止,因为有一些代码在后台运行并且有一个为它注册的处理程序(你通过调用then
方法注册了处理程序)。一旦系统完成读取文件,它将调用处理程序(您传递给then
方法的匿名函数)。只有当没有注册的异步调用的处理程序时,程序才能停止。
现在,您可能已经理解第10行的异常处理程序只能 捕获第3行发生的错误(打开文件时出错)。但是如果第5行发生异常,那么就不能在第10行被捕获,因为catch
处理程序早已消失。
其余的只是正确使用API的问题。如果使用回调,则必须传递成功处理程序和错误处理程序,如果使用Future
,则必须成功调用then
方法处理程序和带有错误处理程序的handleException
方法。