mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
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:
parent
29ee321627
commit
e4f12438a4
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue