Chromium Code Reviews| Index: sdk/lib/async/future.dart |
| diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart |
| index 139f34c6f14062cd787fd6b5df2e6ff29238dc7a..14339c40cdbc9c72a89cd9363592ffef836b3d6f 100644 |
| --- a/sdk/lib/async/future.dart |
| +++ b/sdk/lib/async/future.dart |
| @@ -5,33 +5,98 @@ |
| 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<int> future = getFuture(); |
| * future.then((value) { |
| * print("I received the number $value"); |
| * }); |
| * |
| - * A future may complete by *succeeding* (producing a value) or *failing* |
| - * (producing an error, which may be handled with [catchError]). |
| + * 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: |
| * |
| - * When a future completes, the following actions happen in order: |
| + * Future<int> successor = future.then((int value) { |
|
nweiz
2013/01/11 19:55:06
Nit: indent this line +2 spaces
floitsch
2013/01/11 20:46:09
Done.
|
| + * // 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. |
| + * } |
| + * }); |
|
nweiz
2013/01/11 19:55:06
It would be nice to make the first error-handling
floitsch
2013/01/11 20:46:09
Added a catchError in the first example.
nweiz
2013/01/11 21:39:06
As I mentioned offline, I think it's important to
|
| + * |
| + * 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(); |
|
nweiz
2013/01/11 19:55:06
Nit: I think this whole block (and the ones below)
floitsch
2013/01/11 20:46:09
Done.
|
| + * 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); }); |
| + * }); |
| + * |
| + * 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: |
| * |
| - * 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. |
| + * Future<int> future = foo(); // foo now returns a future. |
| + * future.then((int value) => bar(value)) |
| + * .catchError((e) => 499); |
| * |
| - * [Future]s are usually not created directly, but with [Completer]s. |
| + * 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. |
| + * |
| + * 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 immediately available. |
|
nweiz
2013/01/11 19:55:06
This is a little misleading, since the event loop
floitsch
2013/01/11 20:46:09
Done.
|
| + * |
| + * 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. |
| + * |
| + * 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); |
| } |
| @@ -44,6 +109,8 @@ abstract class Future<T> { |
| * |
| * 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,24 +159,20 @@ 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. |
| - * |
| - * If [onValue] returns a [Future] [:f2:] then [:f:] and [:f2:] are chained. |
| - * That is, [:f:] is completed with the completion value of [:f2:]. |
| + * Returns a new [Future] [:f:] which is completed with the result of |
|
nweiz
2013/01/11 19:55:06
I don't think Dartdoc's formatter understands "[Fu
floitsch
2013/01/11 20:46:09
Pretty sure it does. Will update if not.
nweiz
2013/01/11 21:39:06
You can see in the existing docs that it's not wor
floitsch
2013/01/11 22:39:48
Done.
|
| + * invoking [onValue] (if [this] is completed with a value) or [onError] (if |
| + * [this] is completed with an error). |
| * |
| - * Otherwise [:f:] is completed with the return value of [onValue]. |
| + * 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 |
|
nweiz
2013/01/11 19:55:06
Nit: "result, otherwise" -> "result. Otherwise"
floitsch
2013/01/11 20:46:09
Done.
|
| + * [AsyncError] first. |
| * |
| - * 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 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 [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. |
|
nweiz
2013/01/11 19:55:06
"forwards the error to the top-level exception han
floitsch
2013/01/11 20:46:09
No. to [:f:].
done.
|
| * |
| * 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 |
| @@ -120,23 +183,39 @@ 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. |
| + * When [this] completes with a value, the value is forwarded to [this]' |
|
nweiz
2013/01/11 19:55:06
Nit: "[this]'" -> "[this]'s"
floitsch
2013/01/11 20:46:09
Done.
|
| + * successor unmodified. |
|
nweiz
2013/01/11 19:55:06
It's confusing that in the documentation for then(
floitsch
2013/01/11 20:46:09
Done.
|
| * |
| - * 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 passing it on [this]' successor. |
| * |
| * If [test] is omitted, it defaults to a function that always returns true. |
| * |
| * Example: |
|
nweiz
2013/01/11 19:55:06
There should be a newline after this line, and the
floitsch
2013/01/11 20:46:09
Done.
|
| - * 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)}); |
| @@ -160,6 +239,19 @@ abstract class Future<T> { |
| * 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) { |
|
nweiz
2013/01/11 19:55:06
This is another place where it would be nice to us
floitsch
2013/01/11 20:46:09
Not possible, since it would change the semantics.
nweiz
2013/01/11 21:39:06
Not if catchError() comes before then().
floitsch
2013/01/11 22:39:48
point taken.
For now I will leave it as is. I will
|
| + * action(); |
| + * throw e; |
| + * }); |
| + * } |
| */ |
| Future<T> whenComplete(action()); |