Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1212)

Unified Diff: sdk/lib/async/future.dart

Issue 11852027: Improve documentation for futures. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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());
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698