Don't propagate synchronous Future.wait errors immediately.

Fixes #27249.

R=lrn@google.com

Review URL: https://codereview.chromium.org/2311923002 .
This commit is contained in:
Florian Loitsch 2016-09-06 11:57:43 +02:00
parent 6255638cd0
commit 70c48b9a2e
2 changed files with 46 additions and 3 deletions

View file

@ -322,8 +322,13 @@ abstract class Future<T> {
// The error must have been thrown while iterating over the futures
// list, or while installing a callback handler on the future.
if (remaining == 0 || eagerError) {
// Just complete the error immediately.
result._completeError(e, st);
// Throw a new Future.error.
// Don't just call `result._completeError` since that would propagate
// the error too eagerly, not giving the callers time to install
// error handlers.
// Also, don't use `_asyncCompleteError` since that one doesn't give
// zones the chance to intercept the error.
return new Future.error(e, st);
} else {
// Don't allocate a list for values, thus indicating that there was an
// error.

View file

@ -879,7 +879,6 @@ void testWaitCleanUpError() {
void testWaitSyncError() {
var cms = const Duration(milliseconds: 100);
var cleanups = new List.filled(3, false);
var uncaughts = new List.filled(3, false);
asyncStart();
asyncStart();
runZoned(() {
@ -896,6 +895,43 @@ void testWaitSyncError() {
});
}
void testWaitSyncError2() {
asyncStart();
Future.wait([null]).catchError((e, st) {
// Makes sure that the `catchError` is invoked.
// Regression test: an earlier version of `Future.wait` would propagate
// the error too soon for the code to install an error handler.
// `testWaitSyncError` didn't show this problem, because the `runZoned`
// was already installed.
asyncEnd();
});
}
// Future.wait transforms synchronous errors into asynchronous ones.
// This function tests that zones can intercept them.
void testWaitSyncError3() {
var caughtError;
var count = 0;
AsyncError errorCallback(
Zone self, ZoneDelegate parent, Zone zone, Object error,
StackTrace stackTrace) {
Expect.equals(0, count);
count++;
caughtError = error;
return parent.errorCallback(zone, error, stackTrace);
}
asyncStart();
runZoned(() {
Future.wait([null]).catchError((e, st) {
Expect.identical(e, caughtError);
Expect.equals(1, count);
asyncEnd();
});
}, zoneSpecification: new ZoneSpecification(errorCallback: errorCallback));
}
void testBadFuture() {
var bad = new BadFuture();
// Completing with bad future (then call throws) puts error in result.
@ -1096,6 +1132,8 @@ main() {
testWaitCleanUp();
testWaitCleanUpError();
testWaitSyncError();
testWaitSyncError2();
testWaitSyncError3();
testBadFuture();