Index: pkg/dev_compiler/tool/input_sdk/lib/async/future.dart |
diff --git a/pkg/dev_compiler/tool/input_sdk/lib/async/future.dart b/pkg/dev_compiler/tool/input_sdk/lib/async/future.dart |
index 815f526558764d8b6ab574cdbb53bf525388fac8..49b2460524c27e21f121043531bb7e773e399dfd 100644 |
--- a/pkg/dev_compiler/tool/input_sdk/lib/async/future.dart |
+++ b/pkg/dev_compiler/tool/input_sdk/lib/async/future.dart |
@@ -4,6 +4,46 @@ |
part of dart.async; |
+/// A type representing values that are either `Future<T>` or `T`. |
+/// |
+/// This class declaration is a public stand-in for an internal |
+/// future-or-value generic type. References to this class are resolved to the |
+/// internal type. |
+/// |
+/// It is a compile-time error for any class to extend, mix in or implement |
+/// `FutureOr`. |
+/// |
+/// Note: the `FutureOr<T>` type is interpreted as `dynamic` in non strong-mode. |
+/// |
+/// # Examples |
+/// ``` dart |
+/// // The `Future<T>.then` function takes a callback [f] that returns either |
+/// // an `S` or a `Future<S>`. |
+/// Future<S> then<S>(FutureOr<S> f(T x), ...); |
+/// |
+/// // `Completer<T>.complete` takes either a `T` or `Future<T>`. |
+/// void complete(FutureOr<T> value); |
+/// ``` |
+/// |
+/// # Advanced |
+/// The `FutureOr<int>` type is actually the "type union" of the types `int` and |
+/// `Future<int>`. This type union is defined in such a way that |
+/// `FutureOr<Object>` is both a super- and sub-type of `Object` (sub-type |
+/// because `Object` is one of the types of the union, super-type because |
+/// `Object` is a super-type of both of the types of the union). Together it |
+/// means that `FutureOr<Object>` is equivalent to `Object`. |
+/// |
+/// As a corollary, `FutureOr<Object>` is equivalent to |
+/// `FutureOr<FutureOr<Object>>`, `FutureOr<Future<Object>> is equivalent to |
+/// `Future<Object>`. |
+abstract class FutureOr<T> { |
+ // Private constructor, so that it is not subclassable, mixable, or |
+ // instantiable. |
+ FutureOr._() { |
+ throw new UnsupportedError("FutureOr can't be instantiated"); |
+ } |
+} |
+ |
/** |
* An object representing a delayed computation. |
* |
@@ -225,7 +265,7 @@ abstract class Future<T> { |
_Future<T> result = new _Future<T>(); |
new Timer(duration, () { |
try { |
- result._complete(computation == null ? null : computation()); |
+ result._complete(computation?.call()); |
} catch (e, s) { |
_completeWithErrorCallback(result, e, s); |
} |
@@ -254,11 +294,11 @@ abstract class Future<T> { |
* The call to `cleanUp` should not throw. If it does, the error will be an |
* uncaught asynchronous error. |
*/ |
- static Future<List/*<T>*/> wait/*<T>*/(Iterable<Future/*<T>*/> futures, |
+ static Future<List<T>> wait<T>(Iterable<Future<T>> futures, |
{bool eagerError: false, |
- void cleanUp(/*=T*/ successValue)}) { |
- final _Future<List/*<T>*/> result = new _Future<List/*<T>*/>(); |
- List/*<T>*/ values; // Collects the values. Set to null on error. |
+ void cleanUp(T successValue)}) { |
+ final _Future<List<T>> result = new _Future<List<T>>(); |
+ List<T> values; // Collects the values. Set to null on error. |
int remaining = 0; // How many futures are we waiting for. |
var error; // The first error from a future. |
StackTrace stackTrace; // The stackTrace that came with the error. |
@@ -287,32 +327,56 @@ abstract class Future<T> { |
} |
} |
- // As each future completes, put its value into the corresponding |
- // position in the list of values. |
- for (Future future in futures) { |
- int pos = remaining++; |
- future.then((Object/*=T*/ value) { |
- remaining--; |
- if (values != null) { |
- values[pos] = value; |
- if (remaining == 0) { |
- result._completeWithValue(values); |
- } |
- } else { |
- if (cleanUp != null && value != null) { |
- // Ensure errors from cleanUp are uncaught. |
- new Future.sync(() { cleanUp(value); }); |
- } |
- if (remaining == 0 && !eagerError) { |
- result._completeError(error, stackTrace); |
+ try { |
+ // As each future completes, put its value into the corresponding |
+ // position in the list of values. |
+ for (Future future in futures) { |
+ int pos = remaining; |
+ future.then((T value) { |
+ remaining--; |
+ if (values != null) { |
+ values[pos] = value; |
+ if (remaining == 0) { |
+ result._completeWithValue(values); |
+ } |
+ } else { |
+ if (cleanUp != null && value != null) { |
+ // Ensure errors from cleanUp are uncaught. |
+ new Future.sync(() { cleanUp(value); }); |
+ } |
+ if (remaining == 0 && !eagerError) { |
+ result._completeError(error, stackTrace); |
+ } |
} |
- } |
- }, onError: handleError); |
- } |
- if (remaining == 0) { |
- return new Future.value(const []); |
+ }, onError: handleError); |
+ // Increment the 'remaining' after the call to 'then'. |
+ // If that call throws, we don't expect any future callback from |
+ // the future, and we also don't increment remaining. |
+ remaining++; |
+ } |
+ if (remaining == 0) { |
+ return new Future.value(const []); |
+ } |
+ values = new List<T>(remaining); |
+ } catch (e, st) { |
+ // 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) { |
+ // 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. |
+ // Set error to the caught exception. |
+ error = e; |
+ stackTrace = st; |
+ } |
} |
- values = new List/*<T>*/(remaining); |
return result; |
} |
@@ -326,9 +390,9 @@ abstract class Future<T> { |
* If [futures] is empty, or if none of its futures complete, |
* the returned future never completes. |
*/ |
- static Future/*<T>*/ any/*<T>*/(Iterable<Future/*<T>*/> futures) { |
- var completer = new Completer/*<T>*/.sync(); |
- var onValue = (/*=T*/ value) { |
+ static Future<T> any<T>(Iterable<Future<T>> futures) { |
+ var completer = new Completer<T>.sync(); |
+ var onValue = (T value) { |
if (!completer.isCompleted) completer.complete(value); |
}; |
var onError = (error, stack) { |
@@ -363,18 +427,19 @@ abstract class Future<T> { |
} |
/** |
- * Perform an async operation repeatedly until it returns `false`. |
+ * Performs an async operation repeatedly until it returns `false`. |
* |
- * Runs [f] repeatedly, starting the next iteration only when the [Future] |
- * returned by [f] completes to `true`. Returns a [Future] that completes once |
- * [f] returns `false`. |
+ * The function [f] is called repeatedly while it returns either the [bool] |
+ * value `true` or a [Future] which completes with the value `true`. |
* |
- * The return values of all [Future]s are discarded. Any errors will cause the |
- * iteration to stop and will be piped through the returned [Future]. |
+ * If a call to [f] returns `false` or a [Future] that completes to `false`, |
+ * iteration ends and the future returned by [doWhile] is completed. |
+ * |
+ * If a future returned by [f] completes with an error, iteration ends and |
+ * the future returned by [doWhile] completes with the same error. |
* |
- * The function [f] may return either a [bool] or a [Future] that completes to |
- * a [bool]. If it returns a non-[Future], iteration continues immediately. |
- * Otherwise it waits for the returned [Future] to complete. |
+ * The [f] function must return either a `bool` value or a [Future] completing |
+ * with a `bool` value. |
*/ |
static Future doWhile(f()) { |
_Future doneSignal = new _Future(); |
@@ -432,7 +497,7 @@ abstract class Future<T> { |
* with a `test` parameter, instead of handling both value and error in a |
* single [then] call. |
*/ |
- Future/*<S>*/ then/*<S>*/(onValue(T value), { Function onError }); |
+ Future<S> then<S>(FutureOr<S> onValue(T value), { Function onError }); |
/** |
* Handles errors emitted by this [Future]. |
@@ -495,7 +560,7 @@ abstract class Future<T> { |
// `isCheck` we should also expect functions that take a specific argument. |
// Note: making `catchError` return a `Future<T>` in non-strong mode could be |
// a breaking change. |
- Future/*<T>*/ catchError(Function onError, |
+ Future<T> catchError(Function onError, |
{bool test(Object error)}); |
/** |