Add documentation on Future and Stream showing async programming.

The documentation on `Future` and `Stream` was only showing
the class API, not `async` programming.
Added examples of using `async`, `async*`, `await`, `await for`,
`yield` and `yield*`.

Fixes #47082

Bug: http://dartbug.com/47082
Change-Id: Ib8b4e8642412073e9e6124429d66747c277f89a6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212470
Commit-Queue: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Nate Bosch <nbosch@google.com>
This commit is contained in:
Lasse R.H. Nielsen 2021-09-24 13:07:40 +00:00 committed by commit-bot@chromium.org
parent 29ee321627
commit e4f12438a4
3 changed files with 183 additions and 49 deletions

View file

@ -28,19 +28,19 @@ static method main() → dynamic
Extra constant evaluation status:
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> SymbolConstant(#catchError)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> ListConstant(const <Type*>[])
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:639:13 -> SymbolConstant(#test)
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> SymbolConstant(#whenComplete)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> ListConstant(const <Type*>[])
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:675:13 -> MapConstant(const <Symbol*, dynamic>{})
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> SymbolConstant(#timeout)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> ListConstant(const <Type*>[])
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:698:13 -> SymbolConstant(#onTimeout)
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:602:13 -> SymbolConstant(#then)
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:602:13 -> SymbolConstant(#onError)
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> SymbolConstant(#asStream)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> ListConstant(const <Type*>[])
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> ListConstant(const <dynamic>[])
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:684:13 -> MapConstant(const <Symbol*, dynamic>{})
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:717:13 -> SymbolConstant(#catchError)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:717:13 -> ListConstant(const <Type*>[])
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:717:13 -> SymbolConstant(#test)
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:753:13 -> SymbolConstant(#whenComplete)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:753:13 -> ListConstant(const <Type*>[])
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:753:13 -> MapConstant(const <Symbol*, dynamic>{})
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:776:13 -> SymbolConstant(#timeout)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:776:13 -> ListConstant(const <Type*>[])
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:776:13 -> SymbolConstant(#onTimeout)
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:680:13 -> SymbolConstant(#then)
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:680:13 -> SymbolConstant(#onError)
Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:762:13 -> SymbolConstant(#asStream)
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:762:13 -> ListConstant(const <Type*>[])
Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:762:13 -> ListConstant(const <dynamic>[])
Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/async/future.dart:762:13 -> MapConstant(const <Symbol*, dynamic>{})
Extra constant evaluation: evaluated: 61, effectively constant: 15

View file

@ -7,8 +7,8 @@ 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.
/// future-or-value generic type, which is not a class 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`.
@ -43,35 +43,109 @@ abstract class FutureOr<T> {
}
}
/// An object representing a delayed computation.
/// The result of an asynchronous computation.
///
/// A [Future] is used to represent a potential value, or error,
/// that will be available at some time in the future.
/// Receivers of a [Future] can register callbacks
/// that handle the value or error once it is available.
/// An _asynchronous computation_ cannot provide a result immediately
/// when it is started, unlike a synchronous computation which does compute
/// a result immediately by either returning a value or by throwing.
/// An asynchronous computation may need to wait for something external
/// to the program (reading a file, querying a database, fetching a web page)
/// which takes time.
/// Instead of blocking all computation until the result is available,
/// the asynchronous computation immediately returns a `Future`
/// which will *eventually* "complete" with the result.
///
/// ### Asynchronous programming
///
/// To perform an asynchronous computation, you use an `async` function
/// which always produces a future.
/// Inside such an asynchronous function, you can use the `await` operation
/// to delay execution until another asyncronous computation has a result.
/// While execution of the awaiting function is delayed,
/// the program is not blocked, and can continue doing other things.
///
/// Example:
/// ```dart
/// import "dart:io";
/// Future<bool> fileContains(String path, String needle) async {
/// var haystack = await File(path).readAsString();
/// return haystack.contains(needle);
/// }
/// ```
/// Here the `File.readAsString` method from `dart:io` is an asychronous
/// function returning a `Future<String>`.
/// The `fileContains` function is marked with `async` right before its body,
/// which means that you can use `await` insider it,
/// and that it must return a future.
/// The call to `File(path).readAsString()` initiates reading the file into
/// a string and produces a `Future<String>` which will eventually contain the
/// result.
/// The `await` then waits for that future to complete with a string
/// (or an error, if reading the file fails).
/// While waiting, the program can do other things.
/// When the future completes with a string, the `fileContains` function
/// computes a boolean and returns it, which then completes the original
/// future that it returned when first called.
///
/// If a future completes with an *error*, awaiting that future will
/// (re-)throw that error. In the example here, we can add error checking:
/// ```dart
/// import "dart:io";
/// Future<bool> fileContains(String path, String needle) async {
/// try {
/// var haystack = await File(path).readAsString();
/// return haystack.contains(needle);
/// } on FileSystemException catch (exception, stack) {
/// _myLog.logError(exception, stack);
/// return false;
/// }
/// }
/// ```
/// You use a normal `try`/`catch` to catch the failures of awaited
/// asynchronous computations.
///
/// In general, when writing asynchronous code, you should always await a
/// future when it is produced, and not wait until after another asynchronous
/// delay. That ensures that you are ready to receive any error that the
/// future might produce, which is important because an asynchronous error
/// that no-one is awaiting is an *uncaught* error and may terminate
/// the running program.
///
/// ### Programming with the `Future` API.
///
/// The `Future` class also provides a more direct, low-level functionality
/// for accessing the result that it completes with.
/// The `async` and `await` language features are built on top of this
/// functionality, and it sometimes makes sense to use it directly.
/// There are things that you cannot do by just `await`ing one future at
/// a time.
///
/// With a [Future], you can manually register callbacks
/// that handle the value, or error, once it is available.
/// For example:
/// ```dart
/// 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.
/// Since a [Future] can be completed in two ways,
/// either with a value (if the asynchronous computation succeeded)
/// or with an error (if the computation failed),
/// you can install callbacks for either or both cases.
///
/// In some cases we say that a future is completed with another future.
/// In some cases we say that a future is completed *with another future*.
/// This is a short way of stating that the future is completed in the same way,
/// with the same value or error,
/// as the other future once that completes.
/// Whenever a function in the core library may complete a future
/// as the other future once that other future itself completes.
/// Most functions in the platform libraries that complete a future
/// (for example [Completer.complete] or [Future.value]),
/// then it also accepts another future and does this work for the developer.
/// also accepts another future, and automatically handles forwarding
/// the result to the future being completed.
///
/// The result of registering a pair of callbacks is a 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.
/// The result of registering callbacks is itself a `Future`,
/// which in turn is completed with the result of invoking the
/// corresponding callback with the original future's result.
/// The new future is completed with an error if the invoked callback throws.
/// For example:
/// ```dart
/// Future<int> successor = future.then((int value) {
@ -88,9 +162,9 @@ abstract class FutureOr<T> {
/// });
/// ```
///
/// If a future does not have a successor when it completes with an error,
/// it forwards the error message to an uncaught-error handler.
/// This behavior makes sure that no error is silently dropped.
/// If a future does not have any registered handler when it completes
/// with an error, it forwards the error to an "uncaught-error handler".
/// This behavior ensures 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:
@ -128,9 +202,12 @@ abstract class FutureOr<T> {
///
/// Equivalent asynchronous code, based on futures:
/// ```dart
/// Future<int> future = Future(foo); // Result of foo() as a future.
/// future.then((int value) => bar(value))
/// .catchError((e) => 499);
/// Future<int> asyncValue = Future(foo); // Result of foo() as a future.
/// asyncValue.then((int value) {
/// return bar(value);
/// }).catchError((e) {
/// return 499;
/// });
/// ```
///
/// Similar to the synchronous code, the error handler (registered with
@ -142,7 +219,8 @@ abstract class FutureOr<T> {
/// treated independently and is handled as if it was the only successor.
///
/// A future may also fail to ever complete. In that case, no callbacks are
/// called.
/// called. That situation should generally be avoided if possible, unless
/// it's very clearly documented.
abstract class Future<T> {
/// A `Future<Null>` completed with `null`.
///

View file

@ -18,9 +18,61 @@ typedef void _TimerCallback();
/// When a stream has emitted all its event,
/// a single "done" event will notify the listener that the end has been reached.
///
/// You [listen] on a stream to make it start generating events,
/// and to set up listeners that receive the events.
/// When you listen, you receive a [StreamSubscription] object
/// You produce a stream by calling an `async*` function, which then returns
/// a stream. Consuming that stream will lead the function to emit events
/// until it ends, and the stream closes.
/// You consume a stream either using an `await for` loop, which is available
/// inside an `async` or `async*` function, or forwards its events directly
/// using `yield*` inside an `async*` function.
/// Example:
/// ```dart
/// Stream<T> optionalMap<T>(
/// Stream<T> source , [T Function(T)? convert]) async* {
/// if (convert == null) {
/// yield* source;
/// } else {
/// await for (var event in source) {
/// yield convert(event);
/// }
/// }
/// }
/// ```
/// When this function is called, it immediately returns a `Stream<T>` object.
/// Then nothing further happens until someone tries to consume that stream.
/// At that point, the body of the `async*` function starts running.
/// If the `convert` function was omitted, the `yield*` will listen to the
/// `source` stream and forward all events, date and errors, to the returned
/// stream. When the `source` stream closes, the `yield*` is done,
/// and the `optionalMap` function body ends too. This closes the returned
/// stream.
/// If a `convert` *is* supplied, the function instead listens on the source
/// stream and enters an `await for` loop which
/// repeatedly waits for the next data event.
/// On a data event, it calls `convert` with the value and emits the result
/// on the returned stream.
/// If no error events are emitted by the `source` stream,
/// the loop ends when the `source` stream does,
/// then the `optionalMap` function body completes,
/// which closes the returned stream.
/// On an error event from the `source` stream,
/// the `await for` that error is (re-)thrown which breaks the loop.
/// The error then reaches the end of the `optionalMap` function body,
/// since it's not caught.
/// That makes the error be emitted on the returned stream, which then closes.
///
/// The `Stream` class also provides functionality which allows you to
/// manually listen for events from a stream, or to convert a stream
/// into another stream or into a future.
///
/// The [forEach] function corresponds to the `await for` loop,
/// just as [Iterable.forEach] corresponds to a normal `for`/`in` loop.
/// Like the loop, it will call a function for each data event and break on an
/// error.
///
/// The more low-level [listen] method is what every other method is based on.
/// You call `listen` on a stream to tell it that you want to receive
/// events, and to registers the callbacks which will receive those events.
/// When you call `listen`, you receive a [StreamSubscription] object
/// which is the active object providing the events,
/// and which can be used to stop listening again,
/// or to temporarily pause events from the subscription.
@ -33,19 +85,21 @@ typedef void _TimerCallback();
/// It doesn't start generating events until it has a listener,
/// and it stops sending events when the listener is unsubscribed,
/// even if the source of events could still provide more.
/// The stream created by an `async*` function is a single-subscription stream,
/// but each call to the function creates a new such stream.
///
/// Listening twice on a single-subscription stream is not allowed, even after
/// the first subscription has been canceled.
///
/// Single-subscription streams are generally used for streaming chunks of
/// larger contiguous data like file I/O.
/// larger contiguous data, like file I/O.
///
/// *A broadcast stream* allows any number of listeners, and it fires
/// its events when they are ready, whether there are listeners or not.
///
/// Broadcast streams are used for independent events/observers.
///
/// If several listeners want to listen to a single subscription stream,
/// If several listeners want to listen to a single-subscription stream,
/// use [asBroadcastStream] to create a broadcast stream on top of the
/// non-broadcast stream.
///
@ -77,7 +131,8 @@ typedef void _TimerCallback();
///
/// The default implementation of [isBroadcast] returns false.
/// A broadcast stream inheriting from [Stream] must override [isBroadcast]
/// to return `true`.
/// to return `true` if it wants to signal that it behaves like a broadcast
/// stream.
abstract class Stream<T> {
Stream();
@ -85,6 +140,7 @@ abstract class Stream<T> {
///
/// If mixins become compatible with const constructors, we may use a
/// stream mixin instead of extending Stream from a const class.
/// (They now are compatible. We still consider, but it's not urgent.)
const Stream._internal();
/// Creates an empty broadcast stream.
@ -93,10 +149,10 @@ abstract class Stream<T> {
/// when it's listened to.
const factory Stream.empty() = _EmptyStream<T>;
/// Creates a stream which emits a single data event before completing.
/// Creates a stream which emits a single data event before closing.
///
/// This stream emits a single data event of [value]
/// and then completes with a done event.
/// and then closes with a done event.
///
/// Example:
/// ```dart