I've written a webserver in Dart and have a question regarding exceptions. In my HttpServer requesthandler I've added a try-catch block around the entire method:
try{
...
} catch(e) {
...
}
So I expected that this would prevent any client request from crashing the webserver. The problem is that it can crash when certain exceptions are thrown from within this block (heavily nested in other modules, but still initiated from this block). The following is an example of such a exception:
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)
Why doesn't this get catched in the try-catch block? It is thrown in code that is called from within it (even though it isn't shown in the stacktrace).
I expect that I've missed something about how exceptions work in Dart, so I hope you can enlighten me :)
In order to catch all the exceptions in a block of code you wrap the code in try block and use multiple on-catch instructions to handle some specific exceptions, then use catch to handle all other unexpected exceptions, and finally use finally to run the code that should be invoked after the code in try block and in ...
An unhandled exception is an error in a computer program or application when the code has no appropriate handling exceptions. Learn about the definition and examples of unhandled exceptions, and explore programming and exception handlers. Updated: 01/04/2022.
Dart Exceptions are the run-time error. It is raised when the program gets execution. The program doesn't report the error at compile time when the program runs internally and if Dart compiler found something not appropriate. Then, it reports run-time error and the execution of program is terminated abnormally.
With Future, you have to use the catchError method to deal with exceptions.
Once you realize what is going on, understanding the issue is pretty simple.
The problem is: the semantics of traditional control flow constructs (if
, while
, try/catch/finally
, return
) is purely synchronous. They expect that the program flows just like its source code flows. Take a look at this:
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 }
This program works as a sequence. The line 1 is executed before line 2 which is executed before line 3 etc. Line 5 is executed immediatelly after line 4. Line 11 is executed after line 7, and if an exception happens, line 11 is also executed after line 9. That's what synchronous means.
However, synchronous programs are no longer good enough. Event handling is naturally asynchronous, and you will find events everywhere -- from user interfaces to highly scalable network servers. So if you write
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 }
you have to understand that you are invoking an asynchronous operation (the readAsText
method). In this program, line 2 executes after line 1 and line 3 goes after line 2, but line 4 doesn't execute after line 3. The file reading is invoked asynchronously (think "in the background") and the program continues. So in this situation, after line 3, you will get directly to line 8. And so the return
statement on line 9 (which follows line 8) always returns the 'this will be replaced by the content of the file'
text.
Then, the program goes on and on, until it is done (it exits the main
function). But it doesn't stop, because there is some code running "in the background" and there is a handler registered for it (you registered the handler by calling the then
method). Once the system finishes reading the file, it will call the handler (the anonymous function you passed to the then
method). Only when there is no handler for some asynchronous invocation registered, the program can stop.
And now, you probably understand that the exception handler on line 10 can only catch an error that happens on line 3 (error opening the file). But if an exception happens on line 5, it can't be caught on line 10, because that catch
handler is long gone.
The rest is just a matter of using the API properly. If you work with callbacks, you have to pass an success handler and an error handler, if you work with Future
s, you have to call the then
method with a success handler and the handleException
method with an error handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With