Consider the following code
import 'dart:async';
Future main() async {
try {
print("trying");
await doSomething();
print("success");
} catch (e) {
print("caught");
}
}
Future<int> doSomething() async {
await doSomethingElse();
return 5;
}
Future<int> doSomethingElse() async {
throw new Exception();
}
When run, the exception thrown in doSomethingElse()
is caught up in main()
, and everything works as expected. But, say the person who wrote the doSomething()
method didn't realize that doSomethingElse()
was asynchronous, and instead wrote the follow (note the missing await
).
Future<int> doSomething() async {
doSomethingElse();
return 5;
}
Now the exception isn't caught at all. Rather, the output now looks like this:
trying
success
Unhandled exception:
Uncaught Error: Exception
Stack Trace:
#0 doSomethingElse.<doSomethingElse_async_body> (file:///C:/code/test.dart:19:7)
#1 Future.Future.<anonymous closure> (dart:async/future.dart:118)
<snip>
What's happening is that doSomething()
is returning immediately, and then sometime later, in another context, doSomethingElse()
is throwing its error, stopping all execution immediately. I know that an answer to this might be "Well, don't do that then." but I'm considering cases where I might not have control over the methods I'm calling (say if they are part of a library).
This situation leads to a couple of related questions:
main()
, is there any way I can be certain that my call to doSomething()
won't end with an unhandled exception? Or am I dependent on the author of doSomething()
to make sure all possible exceptions are handled or propagated to the returned Future? Is there a way to attach some sort of global error handler that can catch errors from abandoned Futures?doSomething()
, if I don't want to wait on doSomethingElse()
(say it writes to a log for example, so I neither need the output nor do I need to worry about handling errors). Is there anything I can do to prevent errors in doSomethingElse()
from halting the program other than wrapping every call of it a try/catch block (which can be cumbersome an easily overlooked)?doSomethingElse()
, is there some pattern I can use which allows me to throw Exceptions in a way that callers who wait for the Future to complete can handle that Exception themselves whereas callers that don't wait for the Future don't have to worry about catching the Exception? My best thought in that regard is to return a special object rather than throwing an Exception, but that adds a lot of extra cruft and makes the method much harder use.Note: I'm using async/await syntax here, but the question should be equally relevant for a more strictly Future based construction (where you return a new Future in doSomething()
instead of .then()
ing off the one from doSomethingElse()
Dart uses Future objects to represent asynchronous operations.
An Asynchronous error happens independent of user program like an error in hardware, OS of CPU calls error OBs to handle it.A synchronous error happens when a problem is while processing the user program.
Which type of error prevents the code from executing in Dart? The main objective of the exception is to handle the run-time error and prevent the program from terminating abruptly. Every exception in the Dart is a subtype of the pre-defined class Exception.
Uncaught asynchronous errors are handled to the current zone's error handler. What you are seeing is the root-zone's error handler reporting the error as uncaught, which also terminates the isolate.
What you want is to introduce a different error handler for your code, by running it through runZoned
with an error handler:
import "dart:async";
main() {
runZoned(() async {
try {
print("trying");
await doSomething();
print("success");
} catch (e) {
print("caught");
}
}, onError: (e, s) {
print("uncaught");
});
}
Like Greg pointed in its comment you can use Zones to catch unexpected errors from async code.
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