Michael Fenwick
Michael Fenwick

Reputation: 2494

Dart Errors in multi-level asynchronous code

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:

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()

Upvotes: 13

Views: 2016

Answers (2)

lrn
lrn

Reputation: 71693

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");
  });
}

Upvotes: 12

Alexandre Ardhuin
Alexandre Ardhuin

Reputation: 76223

Like Greg pointed in its comment you can use Zones to catch unexpected errors from async code.

Upvotes: 1

Related Questions