5

我在 Dart 中编写了一个网络服务器,并且有一个关于异常的问题。在我的 HttpServer 请求处理程序中,我在整个方法周围添加了一个 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 中是如何工作的,所以我希望你能启发我:)

4

2 回答 2

7

使用Future,您必须使用catchError方法来处理异常。

于 2012-12-11T20:47:00.660 回答
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 行在第 2 行之前执行,在第 3 行之前执行等等。第 5 行在第 4 行之后立即执行。第 11 行在第 7 行之后执行,如果发生异常,第 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 行。因此第return9 行(在第 8 行之后)的语句总是返回'this will be replaced by the content of the file'文本。

然后,程序继续运行,直到完成(退出main函数)。但它并没有停止,因为有一些代码在“后台”运行,并且有一个为其注册的处理程序(您通过调用该then方法注册了处理程序)。一旦系统完成读取文件,它将调用处理程序(您传递给then方法的匿名函数)。只有当没有注册某些异步调用的处理程序时,程序才能停止。

现在,你可能明白第 10 行的异常处理程序只能捕获第 3 行发生的错误(打开文件时出错)。但是如果第 5 行发生异常,则无法在第 10 行捕获它,因为该catch处理程序早已不复存在。

剩下的只是正确使用 API 的问题。如果使用回调,则必须传递成功处理程序错误处理程序,如果使用Futures,则必须then使用成功处理程序调用方法,使用handleException错误处理程序调用方法。

于 2012-12-12T08:17:29.727 回答