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