| Index: sdk/lib/async/future.dart
|
| diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
|
| index 139f34c6f14062cd787fd6b5df2e6ff29238dc7a..0c4daa0895577b9b0049396ad92f6ee1cecb523f 100644
|
| --- a/sdk/lib/async/future.dart
|
| +++ b/sdk/lib/async/future.dart
|
| @@ -5,33 +5,97 @@
|
| part of dart.async;
|
|
|
| /**
|
| - * A [Future] is used to obtain a value sometime in the future. Receivers of a
|
| - * [Future] can obtain the value by passing a callback to [then]. For example:
|
| + * A [Future] represents a delayed computation. It is used to obtain a not-yet
|
| + * available value, or error, sometime in the future. Receivers of a
|
| + * [Future] can register callbacks that handle the value or error once it is
|
| + * available. For example:
|
| *
|
| - * Future<int> future = getFutureFromSomewhere();
|
| - * future.then((value) {
|
| - * print("I received the number $value");
|
| + * Future<int> future = getFuture();
|
| + * future.then((value) => handleValue(value))
|
| + * .catchError((error) => handleError(error));
|
| + *
|
| + * A [Future] can be completed in two ways: with a value ("the future succeeds")
|
| + * or with an error ("the future fails"). Users can install callbacks for each
|
| + * case. The result of registering a pair of callbacks is a new Future (the
|
| + * "successor") which in turn is completed with the result of invoking the
|
| + * corresponding callback. The successor is completed with an error if the
|
| + * invoked callback throws. For example:
|
| + *
|
| + * Future<int> successor = future.then((int value) {
|
| + * // Invoked when the future is completed with a value.
|
| + * return 42; // The successor is completed with the value 42.
|
| + * },
|
| + * onError: (AsyncError e) {
|
| + * // Invoked when the future is completed with an error.
|
| + * if (canHandle(e)) {
|
| + * return 499; // The successor is completed with the value 499.
|
| + * } else {
|
| + * throw e; // The successor is completed with the error e.
|
| + * }
|
| + * });
|
| + *
|
| + * If a future does not have a successor but is completed with an error, it
|
| + * forwards the error message to the global error-handler. This special casing
|
| + * makes sure that no error is silently dropped. However, it also means that
|
| + * error handlers should be installed early, so that they are present as soon
|
| + * as a future is completed with an error. The following example demonstrates
|
| + * this potential bug:
|
| + *
|
| + * var future = getFuture();
|
| + * new Timer(5, (_) {
|
| + * // The error-handler is only attached 5ms after the future has been
|
| + * // received. If the future fails in the mean-time it will forward the
|
| + * // error to the global error-handler, even though there is code (just
|
| + * // below) to handle the error.
|
| + * future.then((value) { useValue(value); },
|
| + * onError: (e) { handleError(e); });
|
| * });
|
| *
|
| - * A future may complete by *succeeding* (producing a value) or *failing*
|
| - * (producing an error, which may be handled with [catchError]).
|
| + * In general we discourage registering the two callbacks at the same time, but
|
| + * prefer to use [then] with one argument (the value handler), and to use
|
| + * [catchError] for handling errors. The missing callbacks (the error-handler
|
| + * for [then], and the value-handler for [catchError]), are automatically
|
| + * configured to "forward" the value/error. Separating value and error-handling
|
| + * into separate registration calls usually leads to code that is easier to
|
| + * reason about. In fact it makes asynchronous code very similar to synchronous
|
| + * code:
|
| + *
|
| + * // Synchronous code.
|
| + * try {
|
| + * int value = foo();
|
| + * return bar(value);
|
| + * } catch (e) {
|
| + * return 499;
|
| + * }
|
| + *
|
| + * Equivalent asynchronous code, based on futures:
|
| *
|
| - * When a future completes, the following actions happen in order:
|
| + * Future<int> future = foo(); // foo now returns a future.
|
| + * future.then((int value) => bar(value))
|
| + * .catchError((e) => 499);
|
| *
|
| - * 1. if the future suceeded, handlers registered with [then] are called.
|
| - * 2. if the future failed, handlers registered with [catchError] are
|
| - * tested in sequence. Each test returning true is, have its handler
|
| - * called.
|
| - * 4. if the future failed, and no handler registered with [catchError] it
|
| - * is accepting the error, an error is sent to the global error handler.
|
| + * Similar to the synchronous code, the error handler (registered with
|
| + * [catchError]) is handling the errors for exceptions coming from calls to
|
| + * 'foo', as well as 'bar'. This would not be the case if the error-handler was
|
| + * registered at the same time as the value-handler.
|
| *
|
| - * [Future]s are usually not created directly, but with [Completer]s.
|
| + * Futures can have more than one callback-pairs registered. Each successor is
|
| + * treated independently and is handled as if it was the only successor.
|
| */
|
| +// TODO(floitsch): document chaining.
|
| abstract class Future<T> {
|
| - /** A future whose value is immediately available. */
|
| + /**
|
| + * A future whose value is available in the next event-loop iteration.
|
| + *
|
| + * See [Completer]s, for futures with values that are computed asynchronously.
|
| + */
|
| factory Future.immediate(T value) => new _FutureImpl<T>.immediate(value);
|
|
|
| - /** A future that completes with an error. */
|
| + /**
|
| + * A future that completes with an error in the next event-loop iteration.
|
| + *
|
| + * See [Completer]s, for futures with values that are computed asynchronously.
|
| + */
|
| factory Future.immediateError(var error, [Object stackTrace]) {
|
| return new _FutureImpl<T>.immediateError(error, stackTrace);
|
| }
|
| @@ -40,10 +104,13 @@ abstract class Future<T> {
|
| * Creates a future that completes after a delay.
|
| *
|
| * The future will be completed after [milliseconds] have passed with
|
| - * the result of calling [value].
|
| + * the result of calling [value]. If [milliseconds] is 0, it completes at the
|
| + * earliest in the next event-loop iteration.
|
| *
|
| * If calling [value] throws, the created future will complete with the
|
| * error.
|
| + *
|
| + * See [Completer]s, for futures with values that are computed asynchronously.
|
| */
|
| factory Future.delayed(int milliseconds, T value()) {
|
| _FutureImpl<T> future = new _ThenFuture<dynamic, T>((_) => value());
|
| @@ -55,8 +122,8 @@ abstract class Future<T> {
|
| * Wait for all the given futures to complete and collect their values.
|
| *
|
| * Returns a future which will complete once all the futures in a list are
|
| - * complete. If any of the futures in the list completes with an exception,
|
| - * the resulting future also completes with an exception. Otherwise the value
|
| + * complete. If any of the futures in the list completes with an error,
|
| + * the resulting future also completes with an error. Otherwise the value
|
| * of the returned future will be a list of all the values that were produced.
|
| */
|
| static Future<List> wait(Iterable<Future> futures) {
|
| @@ -92,27 +159,23 @@ abstract class Future<T> {
|
| * value. If [this] future is already completed then the invocation of
|
| * [onValue] is delayed until the next event-loop iteration.
|
| *
|
| - * Returns a new [Future] [:f:].
|
| - *
|
| - * If [this] is completed with an error then [:f:] is completed with the same
|
| - * error. If [this] is completed with a value, then [:f:]'s completion value
|
| - * depends on the result of invoking [onValue] with [this]' completion value.
|
| + * Returns a new [Future] `f` which is completed with the result of
|
| + * invoking [onValue] (if [this] completes with a value) or [onError] (if
|
| + * [this] completes with an error).
|
| *
|
| - * If [onValue] returns a [Future] [:f2:] then [:f:] and [:f2:] are chained.
|
| - * That is, [:f:] is completed with the completion value of [:f2:].
|
| + * If the invoked callback throws an exception, the returned future `f` is
|
| + * completed with the error. If the value thrown is an [AsyncError], it is
|
| + * used directly, as the error result. Otherwise it is wrapped in an
|
| + * [AsyncError] first.
|
| *
|
| - * Otherwise [:f:] is completed with the return value of [onValue].
|
| + * If the invoked callback returns a [Future] `f2` then `f` and `f2` are
|
| + * chained. That is, `f` is completed with the completion value of `f2`.
|
| *
|
| - * If [onValue] throws an exception, the returned future will receive the
|
| - * exception. If the value thrown is an [AsyncError], it is used directly,
|
| - * as the error result, otherwise it is wrapped in an [AsyncError] first.
|
| - *
|
| - * If [onError] is provided, it is called if this future completes with an
|
| - * error, and its return value/throw behavior is handled the same way as
|
| - * for [catchError] without a [:test:] argument.
|
| + * If [onError] is not given, it is equivalent to `(e) { throw e; }`. That
|
| + * is, it forwards the error to `f`.
|
| *
|
| * In most cases, it is more readable to use [catchError] separately, possibly
|
| - * with a [:test:] parameter, instead of handling both value and error in a
|
| + * with a `test` parameter, instead of handling both value and error in a
|
| * single [then] call.
|
| */
|
| Future then(onValue(T value), { onError(AsyncError asyncError) });
|
| @@ -120,23 +183,42 @@ abstract class Future<T> {
|
| /**
|
| * Handles errors emitted by this [Future].
|
| *
|
| - * When this future completes with an error, first [test] is called with the
|
| - * error's value.
|
| + * Returns a new [Future] `f`.
|
| + *
|
| + * When [this] completes with a value, the value is forwarded to `f`
|
| + * unmodified. That is, `f` completes with the same value.
|
| *
|
| - * If [test] returns [true], [onError] is called with the error
|
| - * wrapped in an [AsyncError]. The result of [onError] is handled exactly as
|
| - * [then]'s [onValue].
|
| + * When [this] completes with an error, [test] is called with the
|
| + * error's value. If the invocation returns [true], [onError] is called with
|
| + * the error wrapped in an [AsyncError]. The result of [onError] is handled
|
| + * exactly the same as for [then]'s [onError].
|
| *
|
| * If [test] returns false, the exception is not handled by [onError], but is
|
| - * emitted by the returned Future unmodified.
|
| + * thrown unmodified, thus forwarding it to `f`.
|
| *
|
| * If [test] is omitted, it defaults to a function that always returns true.
|
| *
|
| * Example:
|
| - * foo
|
| - * .catchError(..., test: (e) => e is ArgumentError)
|
| - * .catchError(..., test: (e) => e is NoSuchMethodError)
|
| - * .then((v) { ... });
|
| + *
|
| + * foo
|
| + * .catchError(..., test: (e) => e is ArgumentError)
|
| + * .catchError(..., test: (e) => e is NoSuchMethodError)
|
| + * .then((v) { ... });
|
| + *
|
| + * This method is equivalent to:
|
| + *
|
| + * Future catchError(onError(AsyncError asyncError),
|
| + * {bool test(Object error)}) {
|
| + * this.then((v) => v, // Forward the value.
|
| + * // But handle errors, if the [test] succeeds.
|
| + * onError: (AsyncError e) {
|
| + * if (test == null || test(e.error)) {
|
| + * return onError(e);
|
| + * }
|
| + * throw e;
|
| + * });
|
| + * }
|
| + *
|
| */
|
| Future catchError(onError(AsyncError asyncError),
|
| {bool test(Object error)});
|
| @@ -149,17 +231,30 @@ abstract class Future<T> {
|
| *
|
| * This is the asynchronous equivalent of a "finally" block.
|
| *
|
| - * The future returned by this call, [:f:], will complete the same way
|
| + * The future returned by this call, `f`, will complete the same way
|
| * as this future unless an error occurs in the [action] call, or in
|
| * a [Future] returned by the [action] call. If the call to [action]
|
| * does not return a future, its return value is ignored.
|
| *
|
| - * If the call to [action] throws, then [:f:] is completed with the
|
| + * If the call to [action] throws, then `f` is completed with the
|
| * thrown error.
|
| *
|
| - * If the call to [action] returns a [Future], [:f2:], then completion of
|
| - * [:f:] is delayed until [:f2:] completes. If [:f2:] completes with
|
| - * an error, that will be the result of [:f:] too.
|
| + * If the call to [action] returns a [Future], `f2`, then completion of
|
| + * `f` is delayed until `f2` completes. If `f2` completes with
|
| + * an error, that will be the result of `f` too.
|
| + *
|
| + * This method is equivalent to:
|
| + *
|
| + * Future<T> whenComplete(action()) {
|
| + * this.then((v) {
|
| + * action();
|
| + * return v
|
| + * },
|
| + * onError: (AsyncError e) {
|
| + * action();
|
| + * throw e;
|
| + * });
|
| + * }
|
| */
|
| Future<T> whenComplete(action());
|
|
|
| @@ -210,7 +305,7 @@ abstract class Completer<T> {
|
| * Completing a future with an error indicates that an exception was thrown
|
| * while trying to produce a value.
|
| *
|
| - * The argument [exception] should not be [:null:].
|
| + * The argument [exception] should not be `null`.
|
| *
|
| * If [exception] is an [AsyncError], it is used directly as the error
|
| * message sent to the future's listeners, and [stackTrace] is ignored.
|
|
|