f35a3fcce4
`List.unmodifiable` and `Map.unmodifiable` is as badly typed as `List.from` and `Map.from`, but does not have a better-typed `.of` constructor. This adds such, to give a migration target when deprecating the badly typed constructors. The `Future.delayed` with no second argument is also unsafely typed, it fails if the type argument is not nullable. The `Future.pause` creates `Future<void>` instead. CoreLibraryReviewExempt: No new or platform specific behavior. Change-Id: Iba101b3dc62f412003abd501fa042aca0ce63116 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/499280 Commit-Queue: Lasse Nielsen <lrn@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Nate Bosch <nbosch@google.com>
1383 lines
52 KiB
Dart
1383 lines
52 KiB
Dart
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
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, 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`.
|
|
///
|
|
/// ### Examples
|
|
///
|
|
/// ```dart
|
|
/// // The `Future<T>.then` function takes a callback [f] that returns either
|
|
/// // an `S` or a `Future<S>`.
|
|
/// Future<S> then<S>(FutureOr<S> f(T x), ...);
|
|
///
|
|
/// // `Completer<T>.complete` takes either a `T` or `Future<T>`.
|
|
/// void complete(FutureOr<T> value);
|
|
/// ```
|
|
///
|
|
/// ### Advanced
|
|
///
|
|
/// The `FutureOr<int>` type is actually the "type union" of the types `int` and
|
|
/// `Future<int>`. This type union is defined in such a way that
|
|
/// `FutureOr<Object>` is both a super- and sub-type of `Object` (sub-type
|
|
/// because `Object` is one of the types of the union, super-type because
|
|
/// `Object` is a super-type of both of the types of the union). Together it
|
|
/// means that `FutureOr<Object>` is equivalent to `Object`.
|
|
///
|
|
/// As a corollary, `FutureOr<Object>` is equivalent to
|
|
/// `FutureOr<FutureOr<Object>>`, `FutureOr<Future<Object>>` is equivalent to
|
|
/// `Future<Object>`.
|
|
@pragma("vm:entry-point")
|
|
abstract class FutureOr<T> {
|
|
// Private generative constructor, so that it is not subclassable, mixable,
|
|
// or instantiable.
|
|
FutureOr._() {
|
|
throw UnsupportedError("FutureOr cannot be instantiated");
|
|
}
|
|
}
|
|
|
|
/// The result of an asynchronous computation.
|
|
///
|
|
/// 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 asynchronous 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 asynchronous
|
|
/// function returning a `Future<String>`.
|
|
/// The `fileContains` function is marked with `async` right before its body,
|
|
/// which means that you can use `await` inside 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));
|
|
/// ```
|
|
/// 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*.
|
|
/// 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 other future itself completes.
|
|
/// Most functions in the platform libraries that complete a future
|
|
/// (for example [Completer.complete] or [Future.value]),
|
|
/// also accepts another future, and automatically handles forwarding
|
|
/// the result to the future being completed.
|
|
///
|
|
/// 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) {
|
|
/// // Invoked when the future is completed with a value.
|
|
/// return 42; // The successor is completed with the value 42.
|
|
/// },
|
|
/// onError: (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.
|
|
/// }
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// 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:
|
|
/// ```dart
|
|
/// var future = getFuture();
|
|
/// Timer(const Duration(milliseconds: 5), () {
|
|
/// // The error-handler is not attached until 5 ms after the future has
|
|
/// // been received. If the future fails before that, the error is
|
|
/// // forwarded to the global error-handler, even though there is code
|
|
/// // (just below) to eventually handle the error.
|
|
/// future.then((value) { useValue(value); },
|
|
/// onError: (e) { handleError(e); });
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// When registering callbacks, it's often more readable to register the two
|
|
/// callbacks separately, by first using [then] with one argument
|
|
/// (the value handler) and using a second [catchError] for handling errors.
|
|
/// Each of these will forward the result that they don't handle
|
|
/// to their successors, and together they handle both value and error result.
|
|
/// It has the additional benefit of the [catchError] handling errors in the
|
|
/// [then] value callback too.
|
|
/// Using sequential handlers instead of parallel ones often leads to code that
|
|
/// is easier to reason about.
|
|
/// It also makes asynchronous code very similar to synchronous code:
|
|
/// ```dart
|
|
/// // Synchronous code.
|
|
/// try {
|
|
/// int value = foo();
|
|
/// return bar(value);
|
|
/// } catch (e) {
|
|
/// return 499;
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Equivalent asynchronous code, based on futures:
|
|
/// ```dart
|
|
/// 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
|
|
/// [catchError]) is handling any errors thrown by either `foo` or `bar`.
|
|
/// If the error-handler had been registered as the `onError` parameter of
|
|
/// the `then` call, it would not catch errors from the `bar` call.
|
|
///
|
|
/// Futures can have more than one callback-pair registered. Each successor is
|
|
/// treated independently and is handled as if it was the only successor.
|
|
/// The order in which the individual successors are completed is undefined.
|
|
///
|
|
/// A future may also fail to ever complete. In that case, no callbacks are
|
|
/// called. That situation should generally be avoided if possible, unless
|
|
/// it's very clearly documented.
|
|
@pragma("wasm:entry-point")
|
|
@vmIsolateUnsendable
|
|
abstract interface class Future<T> {
|
|
/// A `Future<void>` completed with `null`.
|
|
///
|
|
/// Currently shared with `dart:internal`.
|
|
/// If that future can be removed, then change this back to
|
|
/// `_Future<void>.zoneValue(null, _rootZone);`
|
|
static final _Future<void> _nullFuture = nullFuture as _Future<void>;
|
|
|
|
/// A `Future<bool>` completed with `false`.
|
|
static final _Future<bool> _falseFuture = _Future<bool>.zoneValue(
|
|
false,
|
|
_rootZone,
|
|
);
|
|
|
|
/// Creates a future containing the result of calling [computation]
|
|
/// asynchronously with [Timer.run].
|
|
///
|
|
/// If the result of executing [computation] throws, the returned future is
|
|
/// completed with the error.
|
|
///
|
|
/// If the returned value is itself a [Future], completion of
|
|
/// the created future will wait until the returned future completes,
|
|
/// and will then complete with the same result.
|
|
///
|
|
/// If a non-future value is returned, the returned future is completed
|
|
/// with that value.
|
|
factory Future(FutureOr<T> computation()) {
|
|
_Future<T> result = _Future<T>();
|
|
Timer.run(() {
|
|
FutureOr<T> computationResult;
|
|
try {
|
|
computationResult = computation();
|
|
} catch (e, s) {
|
|
_completeWithErrorCallback(result, e, s);
|
|
return;
|
|
}
|
|
result._complete(computationResult);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/// Creates a future containing the result of calling [computation]
|
|
/// asynchronously with [scheduleMicrotask].
|
|
///
|
|
/// If executing [computation] throws,
|
|
/// the returned future is completed with the thrown error.
|
|
///
|
|
/// If calling [computation] returns a [Future], completion of
|
|
/// the created future will wait until the returned future completes,
|
|
/// and will then complete with the same result.
|
|
///
|
|
/// If calling [computation] returns a non-future value,
|
|
/// the returned future is completed with that value.
|
|
factory Future.microtask(FutureOr<T> computation()) {
|
|
_Future<T> result = _Future<T>();
|
|
scheduleMicrotask(() {
|
|
FutureOr<T> computationResult;
|
|
try {
|
|
computationResult = computation();
|
|
} catch (e, s) {
|
|
_completeWithErrorCallback(result, e, s);
|
|
return;
|
|
}
|
|
result._complete(computationResult);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/// The result of calling [computation] as a future.
|
|
///
|
|
/// Mainly used when working with functions returning a `FutureOr<T>`,
|
|
/// or non-async functions in a non-`async` asynchronous function,
|
|
/// where you want to capture any error in a future.
|
|
///
|
|
/// If calling [computation] throws, the returned future is completed
|
|
/// with the error.
|
|
///
|
|
/// If calling [computation] returns a `Future<T>`, that future is returned.
|
|
///
|
|
/// If calling [computation] returns a non-`Future<T>` value,
|
|
/// a future is created which has been completed with that value.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// Future<int> asyncAdd(
|
|
/// FutureOr<int> Function() a,
|
|
/// FutureOr<int> Function() b,
|
|
/// ) =>
|
|
/// (Future.sync(a), Future.sync(b)).wait.then((ab) => ab.$1 + ab.$2);
|
|
/// ```
|
|
///
|
|
/// To create a future with a known value, use [Future.syncValue] instead,
|
|
/// as `Future.syncValue(12)`.
|
|
factory Future.sync(FutureOr<T> computation()) {
|
|
FutureOr<T> result;
|
|
try {
|
|
result = computation();
|
|
} catch (error, stackTrace) {
|
|
return _Future<T>()
|
|
.._asyncCompleteErrorObject(_interceptCaughtError(error, stackTrace));
|
|
}
|
|
return result is Future<T> ? result : _Future<T>.value(result);
|
|
}
|
|
|
|
/// Creates a future completed with [value].
|
|
///
|
|
/// The future is guaranteed to not have an error.
|
|
///
|
|
/// If a synchronous computation can throw,
|
|
/// rather than doing `Future.syncValue(computation())` and wrapping that in
|
|
/// a `try`/`catch`, you can use [Future.sync] which catches an error
|
|
/// into its returned future, as `Future.sync(() => computation())`.
|
|
@Since("3.10")
|
|
factory Future.syncValue(T value) => _Future<T>().._setValue(value);
|
|
|
|
/// Creates a future completed with [value].
|
|
///
|
|
/// If [value] is a future, the created future waits for the
|
|
/// [value] future to complete, and then completes with the same result.
|
|
/// Since a [value] future can complete with an error, so can the future
|
|
/// created by [Future.value], even if the name suggests otherwise.
|
|
///
|
|
/// If [value] is not a [Future], the created future is completed
|
|
/// with the [value] value,
|
|
/// equivalently to `new Future<T>.sync(() => value)`.
|
|
///
|
|
/// If [value] is omitted or `null`, it is converted to `FutureOr<T>` by
|
|
/// `value as FutureOr<T>`. If `T` is not nullable, then a non-`null` [value]
|
|
/// must be provided, otherwise the construction throws.
|
|
///
|
|
/// Use [Completer] to create a future now and complete it later.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// Future<int> getFuture() {
|
|
/// return Future<int>.value(2021);
|
|
/// }
|
|
///
|
|
/// final result = await getFuture();
|
|
/// ```
|
|
@pragma("vm:entry-point")
|
|
@pragma("vm:prefer-inline")
|
|
factory Future.value([FutureOr<T>? value]) {
|
|
return _Future<T>.immediate(value == null ? value as T : value);
|
|
}
|
|
|
|
/// Creates a future that completes with an error.
|
|
///
|
|
/// The created future will be completed with an error in a future microtask.
|
|
/// This allows enough time for someone to add an error handler on the future.
|
|
/// If an error handler isn't added before the future completes, the error
|
|
/// will be considered unhandled.
|
|
///
|
|
/// Use [Completer] to create a future and complete it later.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// Future<int> getFuture() {
|
|
/// return Future.error(Exception('Issue'));
|
|
/// }
|
|
///
|
|
/// final error = await getFuture(); // Throws.
|
|
/// ```
|
|
factory Future.error(Object error, [StackTrace? stackTrace]) =>
|
|
_Future<T>.immediateError(_interceptUserError(error, stackTrace));
|
|
|
|
/// Creates a future that runs its computation after a delay.
|
|
///
|
|
/// The [computation] will be executed after the given [duration] has passed,
|
|
/// and the future is completed with the result of the computation.
|
|
///
|
|
/// If [computation] returns a future,
|
|
/// the future returned by this constructor will complete with the value or
|
|
/// error of that future, which may not be available until even later.
|
|
///
|
|
/// If the duration is 0 or less,
|
|
/// it completes no sooner than in the next event-loop iteration,
|
|
/// after all microtasks have run.
|
|
/// (The waiting for the delay uses a [Timer] created in the current zone.)
|
|
///
|
|
/// The computation must not be omitted, and must not be null.
|
|
/// Use [Future.pause] instead if you just want to wait for duration.
|
|
/// Until that is reflected in the signature, if [computation] is omitted,
|
|
/// then [T] must be nullable.
|
|
/// Then the computation will be treated as if it was `() => null`,
|
|
/// and the future will eventually complete with the `null` value.
|
|
///
|
|
/// If calling [computation] throws, the created future will complete with the
|
|
/// error.
|
|
///
|
|
/// See also [Completer] for a way to create and complete a future at a
|
|
/// later time that isn't necessarily after a known fixed duration.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// var now = DateTime.now();
|
|
/// var later = await Future.delayed(const Duration(seconds: 1), () {
|
|
/// return DateTime.timestamp();
|
|
/// });
|
|
/// print(now.difference(later)); // At least a second.
|
|
/// ```
|
|
factory Future.delayed(
|
|
Duration duration, [
|
|
FutureOr<T> Function()? computation,
|
|
]) {
|
|
if (computation == null && !typeAcceptsNull<T>()) {
|
|
throw ArgumentError.value(
|
|
null,
|
|
"computation",
|
|
"The type parameter is not nullable",
|
|
);
|
|
}
|
|
_Future<T> result = _Future<T>();
|
|
Timer(duration, () {
|
|
if (computation == null) {
|
|
result._complete(null as T);
|
|
} else {
|
|
FutureOr<T> computationResult;
|
|
try {
|
|
computationResult = computation();
|
|
} catch (e, s) {
|
|
_completeWithErrorCallback(result, e, s);
|
|
return;
|
|
}
|
|
result._complete(computationResult);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/// Creates a [Future] that completes with no result after [duration].
|
|
///
|
|
/// Like [Future.delayed], but does not perform any action,
|
|
/// and cannot complete with an error.
|
|
@Since("3.13")
|
|
static Future<void> pause([Duration duration = Duration.zero]) {
|
|
var result = _Future<void>();
|
|
Zone._current.createTimer(duration, () {
|
|
result._completeWithValue(null);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/// Waits for multiple futures to complete and collects their results.
|
|
///
|
|
/// Returns a future which will complete once all the provided futures
|
|
/// have completed, either with their results, or with an error if any
|
|
/// of the provided futures fail.
|
|
///
|
|
/// The value of the returned future will be a list of all the values that
|
|
/// were produced in the order that the futures are provided by iterating
|
|
/// [futures].
|
|
///
|
|
/// If any future completes with an error,
|
|
/// then the returned future completes with that error.
|
|
/// If further futures also complete with errors, those errors are discarded.
|
|
///
|
|
/// If `eagerError` is true, the returned future completes with an error
|
|
/// immediately on the first error from one of the futures. Otherwise all
|
|
/// futures must complete before the returned future is completed (still with
|
|
/// the first error; the remaining errors are silently dropped).
|
|
///
|
|
/// In the case of an error, [cleanUp] (if provided), is invoked on any
|
|
/// non-null result of successful futures.
|
|
/// This makes it possible to `cleanUp` resources that would otherwise be
|
|
/// lost (since the returned future does not provide access to these values).
|
|
/// The [cleanUp] function is unused if there is no error.
|
|
///
|
|
/// The call to [cleanUp] should not throw. If it does, the error will be an
|
|
/// uncaught asynchronous error.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// void main() async {
|
|
/// var value = await Future.wait([delayedNumber(), delayedString()]);
|
|
/// print(value); // [2, result]
|
|
/// }
|
|
///
|
|
/// Future<int> delayedNumber() async {
|
|
/// await Future.delayed(const Duration(seconds: 2));
|
|
/// return 2;
|
|
/// }
|
|
///
|
|
/// Future<String> delayedString() async {
|
|
/// await Future.delayed(const Duration(seconds: 2));
|
|
/// return 'result';
|
|
/// }
|
|
/// ```
|
|
static Future<List<T>> wait<T>(
|
|
Iterable<Future<T>> futures, {
|
|
bool eagerError = false,
|
|
void cleanUp(T successValue)?,
|
|
}) {
|
|
@pragma('vm:awaiter-link')
|
|
final _Future<List<T>> _future = _Future<List<T>>();
|
|
List<T?>? values; // Collects the values. Set to null on error.
|
|
int remaining = 0; // How many futures are we waiting for.
|
|
Object? error; // The first error from a future.
|
|
StackTrace? stackTrace; // The stackTrace that came with the error.
|
|
|
|
// Handle an error from any of the futures.
|
|
void handleError(Object theError, StackTrace theStackTrace) {
|
|
var remainingResults = --remaining;
|
|
List<T?>? valueList = values;
|
|
if (valueList != null) {
|
|
// First error, set state to represent error having already happened.
|
|
values = null;
|
|
error = theError;
|
|
stackTrace = theStackTrace;
|
|
// Then clean up any already successfully produced results.
|
|
if (cleanUp != null) {
|
|
for (var value in valueList) {
|
|
if (value != null) {
|
|
// Ensure errors from `cleanUp` are uncaught.
|
|
T cleanUpValue = value;
|
|
Future.sync(() {
|
|
cleanUp(cleanUpValue);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if (remainingResults == 0 || eagerError) {
|
|
_future._completeError(theError, theStackTrace);
|
|
}
|
|
} else {
|
|
// Not the first error.
|
|
if (remainingResults == 0 && !eagerError) {
|
|
// Last future completed, non-eagerly report the first error.
|
|
_future._completeError(error!, stackTrace!);
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
// As each future completes, put its value into the corresponding
|
|
// position in the list of values.
|
|
for (var future in futures) {
|
|
int pos = remaining;
|
|
future.then((T value) {
|
|
var remainingResults = --remaining;
|
|
List<T?>? valueList = values;
|
|
if (valueList != null) {
|
|
// No errors yet.
|
|
assert(valueList[pos] == null);
|
|
valueList[pos] = value;
|
|
if (remainingResults == 0) {
|
|
_future._completeWithValue([
|
|
for (var value in valueList) value as T,
|
|
]);
|
|
}
|
|
} else {
|
|
// Prior error, clean-up this value if necessary.
|
|
if (cleanUp != null && value != null) {
|
|
// Ensure errors from cleanUp are uncaught.
|
|
Future.sync(() {
|
|
cleanUp(value);
|
|
});
|
|
}
|
|
if (remainingResults == 0 && !eagerError) {
|
|
// Last future completed, non-eagerly report the first error.
|
|
_future._completeError(error!, stackTrace!);
|
|
}
|
|
}
|
|
}, onError: handleError);
|
|
// Increment the 'remaining' after the call to 'then'.
|
|
// If that call throws, we don't expect any future callback from
|
|
// the future, and we also don't increment remaining.
|
|
remaining++;
|
|
}
|
|
if (remaining == 0) {
|
|
// No elements in iterable.
|
|
return _future.._completeWithValue(<T>[]);
|
|
}
|
|
values = List<T?>.filled(remaining, null);
|
|
} catch (e, s) {
|
|
// The error must have been thrown while iterating over the futures
|
|
// list, or while installing a callback handler on the future.
|
|
// This is a breach of the `Future` protocol, but we try to handle it
|
|
// gracefully.
|
|
if (remaining == 0 || eagerError) {
|
|
// Complete asynchronously to give receiver time to handle
|
|
// the result.
|
|
return _future.._asyncCompleteErrorObject(_interceptCaughtError(e, s));
|
|
} else {
|
|
// Don't allocate a list for values, thus indicating that there was an
|
|
// error.
|
|
// Set error to the caught exception.
|
|
error = e;
|
|
stackTrace = s;
|
|
}
|
|
}
|
|
return _future;
|
|
}
|
|
|
|
/// Returns the result of the first future in [futures] to complete.
|
|
///
|
|
/// The returned future is completed with the result of the first
|
|
/// future in [futures] to report that it is complete,
|
|
/// whether it's with a value or an error.
|
|
/// The results of all the other futures are discarded.
|
|
///
|
|
/// If [futures] is empty, or if none of its futures complete,
|
|
/// the returned future never completes.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// void main() async {
|
|
/// final result =
|
|
/// await Future.any([slowInt(), delayedString(), fastInt()]);
|
|
/// // The future of fastInt completes first, others are ignored.
|
|
/// print(result); // 3
|
|
/// }
|
|
/// Future<int> slowInt() async {
|
|
/// await Future.delayed(const Duration(seconds: 2));
|
|
/// return 2;
|
|
/// }
|
|
///
|
|
/// Future<String> delayedString() async {
|
|
/// await Future.delayed(const Duration(seconds: 2));
|
|
/// throw TimeoutException('Time has passed');
|
|
/// }
|
|
///
|
|
/// Future<int> fastInt() async {
|
|
/// await Future.delayed(const Duration(seconds: 1));
|
|
/// return 3;
|
|
/// }
|
|
/// ```
|
|
static Future<T> any<T>(Iterable<Future<T>> futures) {
|
|
var completer = Completer<T>.sync();
|
|
void onValue(T value) {
|
|
if (!completer.isCompleted) completer.complete(value);
|
|
}
|
|
|
|
void onError(Object error, StackTrace stack) {
|
|
if (!completer.isCompleted) completer.completeError(error, stack);
|
|
}
|
|
|
|
for (var future in futures) {
|
|
future.then(onValue, onError: onError);
|
|
}
|
|
return completer.future;
|
|
}
|
|
|
|
/// Performs an action for each element of the iterable, in turn.
|
|
///
|
|
/// The [action] may be either synchronous or asynchronous.
|
|
///
|
|
/// Calls [action] with each element in [elements] in order.
|
|
/// If the call to [action] returns a `Future<T>`, the iteration waits
|
|
/// until the future is completed before continuing with the next element.
|
|
///
|
|
/// Returns a [Future] that completes with `null` when all elements have been
|
|
/// processed.
|
|
///
|
|
/// Non-[Future] return values, and completion-values of returned [Future]s,
|
|
/// are discarded.
|
|
///
|
|
/// Any error from [action], synchronous or asynchronous,
|
|
/// will stop the iteration and be reported in the returned [Future].
|
|
static Future<void> forEach<T>(
|
|
Iterable<T> elements,
|
|
FutureOr action(T element),
|
|
) {
|
|
var iterator = elements.iterator;
|
|
return doWhile(() {
|
|
if (!iterator.moveNext()) return false;
|
|
var result = action(iterator.current);
|
|
if (result is Future) return result.then(_kTrue);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
// Constant `true` function, used as callback by [forEach].
|
|
static bool _kTrue(Object? _) => true;
|
|
|
|
/// Performs an operation repeatedly until it returns `false`.
|
|
///
|
|
/// The operation, [action], may be either synchronous or asynchronous.
|
|
///
|
|
/// The operation is called repeatedly as long as it returns either the [bool]
|
|
/// value `true` or a `Future<bool>` which completes with the value `true`.
|
|
///
|
|
/// If a call to [action] returns `false` or a [Future] that completes to
|
|
/// `false`, iteration ends and the future returned by [doWhile] is completed
|
|
/// with a `null` value.
|
|
///
|
|
/// If a call to [action] throws or a future returned by [action] completes
|
|
/// with an error, iteration ends and the future returned by [doWhile]
|
|
/// completes with the same error.
|
|
///
|
|
/// Calls to [action] may happen at any time,
|
|
/// including immediately after calling `doWhile`.
|
|
/// The only restriction is a new call to [action] won't happen before
|
|
/// the previous call has returned, and if it returned a `Future<bool>`, not
|
|
/// until that future has completed.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// void main() async {
|
|
/// var value = 0;
|
|
/// await Future.doWhile(() async {
|
|
/// value++;
|
|
/// await Future.delayed(const Duration(seconds: 1));
|
|
/// if (value == 3) {
|
|
/// print('Finished with $value');
|
|
/// return false;
|
|
/// }
|
|
/// return true;
|
|
/// });
|
|
/// }
|
|
/// // Outputs: 'Finished with 3'
|
|
/// ```
|
|
static Future<void> doWhile(FutureOr<bool> action()) {
|
|
@pragma('vm:awaiter-link')
|
|
_Future<void> doneSignal = _Future<void>();
|
|
late void Function(bool) nextIteration;
|
|
// Bind this callback explicitly so that each iteration isn't bound in the
|
|
// context of all the previous iterations' callbacks.
|
|
// This avoids, e.g., deeply nested stack traces from the stack trace
|
|
// package.
|
|
nextIteration = Zone.current.bindUnaryCallbackGuarded((bool keepGoing) {
|
|
while (keepGoing) {
|
|
FutureOr<bool> result;
|
|
try {
|
|
result = action();
|
|
} catch (error, stackTrace) {
|
|
// Cannot use `_completeWithErrorCallback` because it completes
|
|
// the future synchronously.
|
|
doneSignal._asyncCompleteErrorObject(
|
|
_interceptCaughtError(error, stackTrace),
|
|
);
|
|
return;
|
|
}
|
|
if (result is Future<bool>) {
|
|
result.then(nextIteration, onError: doneSignal._completeError);
|
|
return;
|
|
}
|
|
keepGoing = result;
|
|
}
|
|
doneSignal._complete(null);
|
|
});
|
|
nextIteration(true);
|
|
return doneSignal;
|
|
}
|
|
|
|
/// Register callbacks to be called when this future completes.
|
|
///
|
|
/// When this future completes with a value,
|
|
/// the [onValue] callback will be called with that value.
|
|
/// If this future is already completed, the callback will not be called
|
|
/// immediately, but will be scheduled in a later microtask.
|
|
///
|
|
/// If [onError] is provided, and this future completes with an error,
|
|
/// the `onError` callback is called with that error and its stack trace.
|
|
/// The `onError` callback must accept either one argument or two arguments
|
|
/// where the latter is a [StackTrace].
|
|
/// If `onError` accepts two arguments,
|
|
/// it is called with both the error and the stack trace,
|
|
/// otherwise it is called with just the error object.
|
|
/// The `onError` callback must return a value or future that can be used
|
|
/// to complete the returned future, so it must be something assignable to
|
|
/// `FutureOr<R>`.
|
|
///
|
|
/// Returns a new [Future]
|
|
/// which is completed with the result of the call to `onValue`
|
|
/// (if this future completes with a value)
|
|
/// or to `onError` (if this future completes with an error).
|
|
///
|
|
/// If the invoked callback throws,
|
|
/// the returned future is completed with the thrown error
|
|
/// and a stack trace for the error.
|
|
/// In the case of `onError`,
|
|
/// if the exception thrown is `identical` to the error argument to `onError`,
|
|
/// and it is thrown *synchronously*
|
|
/// the throw is considered a rethrow,
|
|
/// and the original stack trace is used instead.
|
|
/// To rethrow with the same stack trace in an asynchronous callback,
|
|
/// use [Error.throwWithStackTrace].
|
|
///
|
|
/// If the callback returns a [Future],
|
|
/// the future returned by `then` will be completed with
|
|
/// the same result as the future returned by the callback.
|
|
///
|
|
/// If [onError] is not given, and this future completes with an error,
|
|
/// the error is forwarded directly to the returned future.
|
|
///
|
|
/// 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 single [then] call.
|
|
///
|
|
/// Note that futures don't delay reporting of errors until listeners are
|
|
/// added. If the first `then` or `catchError` call happens
|
|
/// after this future has completed with an error,
|
|
/// then the error is reported as unhandled error.
|
|
/// See the description on [Future].
|
|
Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
|
|
|
|
/// Handles errors emitted by this [Future].
|
|
///
|
|
/// This is the asynchronous equivalent of a "catch" block.
|
|
///
|
|
/// Returns a new [Future] that will be completed with either the result of
|
|
/// this future or the result of calling the `onError` callback.
|
|
///
|
|
/// If this future completes with a value,
|
|
/// the returned future completes with the same value.
|
|
///
|
|
/// If this future completes with an error,
|
|
/// then [test] is first called with the error value.
|
|
///
|
|
/// If `test` returns false, the exception is not handled by this `catchError`,
|
|
/// and the returned future completes with the same error and stack trace
|
|
/// as this future.
|
|
///
|
|
/// If `test` returns `true`,
|
|
/// [onError] is called with the error and possibly stack trace,
|
|
/// and the returned future is completed with the result of this call
|
|
/// in exactly the same way as for [then]'s `onError`.
|
|
///
|
|
/// If `test` is omitted, it defaults to a function that always returns true.
|
|
/// The `test` function should not throw, but if it does, it is handled as
|
|
/// if the `onError` function had thrown.
|
|
///
|
|
/// Note that futures don't delay reporting of errors until listeners are
|
|
/// added. If the first `catchError` (or `then`) call happens after this future
|
|
/// has completed with an error then the error is reported as unhandled error.
|
|
/// See the description on [Future].
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// Future.delayed(
|
|
/// const Duration(seconds: 1),
|
|
/// () => throw 401,
|
|
/// ).then((value) {
|
|
/// throw 'Unreachable';
|
|
/// }).catchError((err) {
|
|
/// print('Error: $err'); // Prints 401.
|
|
/// }, test: (error) {
|
|
/// return error is int && error >= 400;
|
|
/// });
|
|
/// ```
|
|
// The `Function` below stands for one of two types:
|
|
// - (dynamic) -> FutureOr<T>
|
|
// - (dynamic, StackTrace) -> FutureOr<T>
|
|
// Given that there is a `test` function that is usually used to do an
|
|
// `is` check, we should also expect functions that take a specific argument.
|
|
Future<T> catchError(Function onError, {bool test(Object error)?});
|
|
|
|
/// Registers a function to be called when this future completes.
|
|
///
|
|
/// The [action] function is called when this future completes, whether it
|
|
/// does so with a value or with an error.
|
|
///
|
|
/// This is the asynchronous equivalent of a "finally" block.
|
|
///
|
|
/// The future returned by this call, `f`, will complete the same way
|
|
/// as this future unless an error occurs in the [action] call, or in
|
|
/// a [Future] returned by the [action] call. If the call to [action]
|
|
/// does not return a future, its return value is ignored.
|
|
///
|
|
/// If the call to [action] throws, then `f` is completed with the
|
|
/// thrown error.
|
|
///
|
|
/// 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. The value of `f2` is always
|
|
/// ignored.
|
|
///
|
|
/// This method is equivalent to:
|
|
/// ```dart
|
|
/// Future<T> whenComplete(action()) {
|
|
/// return this.then((v) {
|
|
/// var f2 = action();
|
|
/// if (f2 is Future) return f2.then((_) => v);
|
|
/// return v;
|
|
/// }, onError: (e) {
|
|
/// var f2 = action();
|
|
/// if (f2 is Future) return f2.then((_) { throw e; });
|
|
/// throw e;
|
|
/// });
|
|
/// }
|
|
/// ```
|
|
/// Example:
|
|
/// ```dart
|
|
/// void main() async {
|
|
/// var value =
|
|
/// await waitTask().whenComplete(() => print('do something here'));
|
|
/// // Prints "do something here" after waitTask() completed.
|
|
/// print(value); // Prints "done"
|
|
/// }
|
|
///
|
|
/// Future<String> waitTask() {
|
|
/// Future.delayed(const Duration(seconds: 5));
|
|
/// return Future.value('done');
|
|
/// }
|
|
/// // Outputs: 'do some work here' after waitTask is completed.
|
|
/// ```
|
|
Future<T> whenComplete(FutureOr<void> action());
|
|
|
|
/// Creates a [Stream] containing the result of this future.
|
|
///
|
|
/// The stream will produce single data or error event containing the
|
|
/// completion result of this future, and then it will close with a
|
|
/// done event.
|
|
///
|
|
/// If the future never completes, the stream will not produce any events.
|
|
Stream<T> asStream();
|
|
|
|
/// Stop waiting for this future after [timeLimit] has passed.
|
|
///
|
|
/// Creates a new _timeout future_ that completes
|
|
/// with the same result as this future, the _source future_,
|
|
/// *if* the source future completes in time.
|
|
///
|
|
/// If the source future does not complete before [timeLimit] has passed,
|
|
/// the [onTimeout] action is executed,
|
|
/// and its result (whether it returns or throws)
|
|
/// is used as the result of the timeout future.
|
|
/// The [onTimeout] function must return a [T] or a `Future<T>`.
|
|
/// If [onTimeout] returns a future, the _alternative result future_,
|
|
/// the eventual result of the alternative result future is used
|
|
/// to complete the timeout future,
|
|
/// even if the source future completes
|
|
/// before the alternative result future.
|
|
/// It only matters that the source future did not complete in time.
|
|
///
|
|
/// If `onTimeout` is omitted, a timeout will cause the returned future to
|
|
/// complete with a [TimeoutException].
|
|
///
|
|
/// In either case, the source future can still complete normally
|
|
/// at a later time.
|
|
/// It just won't be used as the result of the timeout future
|
|
/// unless it completes within the time bound.
|
|
/// Even if the source future completes with an error,
|
|
/// if that error happens after [timeLimit] has passed,
|
|
/// the error is ignored, just like a value result would be.
|
|
///
|
|
/// Examples:
|
|
/// ```dart
|
|
/// void main() async {
|
|
/// var result =
|
|
/// await waitTask("completed").timeout(const Duration(seconds: 10));
|
|
/// print(result); // Prints "completed" after 5 seconds.
|
|
///
|
|
/// result = await waitTask("completed")
|
|
/// .timeout(const Duration(seconds: 1), onTimeout: () => "timeout");
|
|
/// print(result); // Prints "timeout" after 1 second.
|
|
///
|
|
/// result = await waitTask("first").timeout(const Duration(seconds: 2),
|
|
/// onTimeout: () => waitTask("second"));
|
|
/// print(result); // Prints "second" after 7 seconds.
|
|
///
|
|
/// try {
|
|
/// await waitTask("completed").timeout(const Duration(seconds: 2));
|
|
/// } on TimeoutException {
|
|
/// print("throws"); // Prints "throws" after 2 seconds.
|
|
/// }
|
|
///
|
|
/// var printFuture = waitPrint();
|
|
/// await printFuture.timeout(const Duration(seconds: 2), onTimeout: () {
|
|
/// print("timeout"); // Prints "timeout" after 2 seconds.
|
|
/// });
|
|
/// await printFuture; // Prints "printed" after additional 3 seconds.
|
|
///
|
|
/// try {
|
|
/// await waitThrow("error").timeout(const Duration(seconds: 2));
|
|
/// } on TimeoutException {
|
|
/// print("throws"); // Prints "throws" after 2 seconds.
|
|
/// }
|
|
/// // StateError is ignored
|
|
/// }
|
|
///
|
|
/// /// Returns [string] after five seconds.
|
|
/// Future<String> waitTask(String string) async {
|
|
/// await Future.delayed(const Duration(seconds: 5));
|
|
/// return string;
|
|
/// }
|
|
///
|
|
/// /// Prints "printed" after five seconds.
|
|
/// Future<void> waitPrint() async {
|
|
/// await Future.delayed(const Duration(seconds: 5));
|
|
/// print("printed");
|
|
/// }
|
|
/// /// Throws a [StateError] with [message] after five seconds.
|
|
/// Future<void> waitThrow(String message) async {
|
|
/// await Future.delayed(const Duration(seconds: 5));
|
|
/// throw Exception(message);
|
|
/// }
|
|
/// ```
|
|
Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?});
|
|
}
|
|
|
|
/// Explicitly ignores a future.
|
|
///
|
|
/// Not all futures need to be awaited.
|
|
/// The Dart linter has an optional
|
|
/// ["unawaited futures" lint](https://dart.dev/lints/unawaited_futures)
|
|
/// which enforces that potential futures
|
|
/// (expressions with a static type of [Future] or `Future?`)
|
|
/// in asynchronous functions are handled *somehow*.
|
|
/// If a particular future value doesn't need to be awaited,
|
|
/// you can call `unawaited(...)` with it, which will avoid the lint,
|
|
/// simply because the expression no longer has type [Future].
|
|
/// Using `unawaited` has no other effect.
|
|
/// You should use `unawaited` to convey the *intention* of
|
|
/// deliberately not waiting for the future.
|
|
///
|
|
/// If the future completes with an error,
|
|
/// it was likely a mistake to not await it.
|
|
/// That error will still occur and will be considered unhandled
|
|
/// unless the same future is awaited (or otherwise handled) elsewhere too.
|
|
/// Because of that, `unawaited` should only be used for futures that
|
|
/// are *expected* to complete with a value.
|
|
/// You can use [FutureExtensions.ignore] if you also don't want to know
|
|
/// about errors from this future.
|
|
@Since("2.15")
|
|
void unawaited(Future<void>? future) {}
|
|
|
|
/// Convenience methods on futures.
|
|
///
|
|
/// Adds functionality to futures which makes it easier to
|
|
/// write well-typed asynchronous code.
|
|
extension FutureExtensions<T> on Future<T> {
|
|
/// Handles errors on this future.
|
|
///
|
|
/// Catches errors of type [E] that this future complete with.
|
|
/// If [test] is supplied, only catches errors of type [E]
|
|
/// where [test] returns `true`.
|
|
/// If [E] is [Object], then all errors are potentially caught,
|
|
/// depending only on a supplied [test].
|
|
///
|
|
/// If the error is caught,
|
|
/// the returned future completes with the result of calling [handleError]
|
|
/// with the error and stack trace.
|
|
/// This result must be a value of the same type that this future
|
|
/// could otherwise complete with.
|
|
/// For example, if this future cannot complete with `null`,
|
|
/// then [handleError] also cannot return `null`.
|
|
/// Example:
|
|
/// ```dart
|
|
/// Future<T> retryOperation<T>(Future<T> operation(), T onFailure()) =>
|
|
/// operation().onError<RetryException>((e, s) {
|
|
/// if (e.canRetry) {
|
|
/// return retryOperation(operation, onFailure);
|
|
/// }
|
|
/// return onFailure();
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// If [handleError] throws, the returned future completes
|
|
/// with the thrown error and stack trace,
|
|
/// except that if it throws the *same* error object again,
|
|
/// then it is considered a "rethrow"
|
|
/// and the original stack trace is retained.
|
|
/// This can be used as an alternative to skipping the
|
|
/// error in [test].
|
|
/// Example:
|
|
/// ```dart
|
|
/// // Unwraps an exceptions cause, if it has one.
|
|
/// someFuture.onError<SomeException>((e, _) {
|
|
/// throw e.cause ?? e;
|
|
/// });
|
|
/// // vs.
|
|
/// someFuture.onError<SomeException>((e, _) {
|
|
/// throw e.cause!;
|
|
/// }, test: (e) => e.cause != null);
|
|
/// ```
|
|
///
|
|
/// If the error is not caught, the returned future
|
|
/// completes with the same result, value or error,
|
|
/// as this future.
|
|
///
|
|
/// This method is effectively a more precisely typed version
|
|
/// of [Future.catchError].
|
|
/// It makes it easy to catch specific error types,
|
|
/// and requires a correctly typed error handler function,
|
|
/// rather than just [Function].
|
|
/// Because of this, the error handlers must accept
|
|
/// the stack trace argument.
|
|
Future<T> onError<E extends Object>(
|
|
FutureOr<T> handleError(E error, StackTrace stackTrace), {
|
|
bool test(E error)?,
|
|
}) {
|
|
FutureOr<T> onError(Object error, StackTrace stackTrace) {
|
|
if (error is! E || test != null && !test(error)) {
|
|
// Counts as rethrow, preserves stack trace.
|
|
throw error;
|
|
}
|
|
return handleError(error, stackTrace);
|
|
}
|
|
|
|
if (this is _Future<Object?>) {
|
|
// Internal method working like `catchError`,
|
|
// but allows specifying a different result future type.
|
|
return unsafeCast<_Future<T>>(this)._safeOnError<T>(onError);
|
|
}
|
|
|
|
return this.then<T>((T value) => value, onError: onError);
|
|
}
|
|
|
|
/// Completely ignores this future and its result.
|
|
///
|
|
/// Not all futures are important, not even if they contain errors,
|
|
/// for example if a request was made, but the response is no longer needed.
|
|
/// Simply ignoring a future can result in uncaught asynchronous errors.
|
|
/// This method instead handles (and ignores) any values or errors
|
|
/// coming from this future, making it safe to otherwise ignore
|
|
/// the future.
|
|
///
|
|
/// Use `ignore` to signal that the result of the future is
|
|
/// no longer important to the program, not even if it's an error.
|
|
/// If you merely want to silence the
|
|
/// ["unawaited futures" lint](https://dart.dev/lints/unawaited_futures),
|
|
/// use the [unawaited] function instead.
|
|
/// That will ensure that an unexpected error is still reported.
|
|
@Since("2.14")
|
|
void ignore() {
|
|
var self = this;
|
|
if (self is _Future<T>) {
|
|
self._ignore();
|
|
} else {
|
|
self.then<void>(_ignore, onError: _ignore);
|
|
}
|
|
}
|
|
|
|
static void _ignore(Object? _, [Object? __]) {}
|
|
}
|
|
|
|
/// Thrown when a scheduled timeout happens while waiting for an async result.
|
|
class TimeoutException implements Exception {
|
|
/// Description of the cause of the timeout.
|
|
final String? message;
|
|
|
|
/// The duration that was exceeded.
|
|
final Duration? duration;
|
|
|
|
TimeoutException(this.message, [this.duration]);
|
|
|
|
String toString() {
|
|
String result = "TimeoutException";
|
|
if (duration != null) result = "TimeoutException after $duration";
|
|
if (message != null) result = "$result: $message";
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// A way to produce Future objects and to complete them later
|
|
/// with a value or error.
|
|
///
|
|
/// Most of the time, the simplest way to create a future is to just use
|
|
/// one of the [Future] constructors to capture the result of a single
|
|
/// asynchronous computation:
|
|
/// ```dart
|
|
/// Future(() { doSomething(); return result; });
|
|
/// ```
|
|
/// or, if the future represents the result of a sequence of asynchronous
|
|
/// computations, they can be chained using [Future.then] or similar functions
|
|
/// on [Future]:
|
|
/// ```dart
|
|
/// Future doStuff(){
|
|
/// return someAsyncOperation().then((result) {
|
|
/// return someOtherAsyncOperation(result);
|
|
/// });
|
|
/// }
|
|
/// ```
|
|
/// If you do need to create a Future from scratch — for example,
|
|
/// when you're converting a callback-based API into a Future-based
|
|
/// one — you can use a Completer as follows:
|
|
/// ```dart
|
|
/// class AsyncOperation {
|
|
/// final Completer _completer = new Completer();
|
|
///
|
|
/// Future<T> doOperation() {
|
|
/// _startOperation();
|
|
/// return _completer.future; // Send future object back to client.
|
|
/// }
|
|
///
|
|
/// // Something calls this when the value is ready.
|
|
/// void _finishOperation(T result) {
|
|
/// _completer.complete(result);
|
|
/// }
|
|
///
|
|
/// // If something goes wrong, call this.
|
|
/// void _errorHappened(error) {
|
|
/// _completer.completeError(error);
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
@vmIsolateUnsendable
|
|
abstract interface class Completer<T> {
|
|
/// Creates a new completer.
|
|
///
|
|
/// The general workflow for creating a new future is to 1) create a
|
|
/// new completer, 2) hand out its future, and, at a later point, 3) invoke
|
|
/// either [complete] or [completeError].
|
|
///
|
|
/// The completer completes the future asynchronously. That means that
|
|
/// callbacks registered on the future are not called immediately when
|
|
/// [complete] or [completeError] is called. Instead the callbacks are
|
|
/// delayed until a later microtask.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// var completer = new Completer();
|
|
/// handOut(completer.future);
|
|
/// later: {
|
|
/// completer.complete('completion value');
|
|
/// }
|
|
/// ```
|
|
factory Completer() => _AsyncCompleter<T>();
|
|
|
|
/// Completes the future synchronously.
|
|
///
|
|
/// This constructor should be avoided unless the completion of the future is
|
|
/// known to be the final result of another asynchronous operation. If in doubt
|
|
/// use the default [Completer] constructor.
|
|
///
|
|
/// Using an normal, asynchronous, completer will never give the wrong
|
|
/// behavior, but using a synchronous completer incorrectly can cause
|
|
/// otherwise correct programs to break.
|
|
///
|
|
/// A synchronous completer is only intended for optimizing event
|
|
/// propagation when one asynchronous event immediately triggers another.
|
|
/// It should not be used unless the calls to [complete] and [completeError]
|
|
/// are guaranteed to occur in places where it won't break `Future` invariants.
|
|
///
|
|
/// Completing synchronously means that the completer's future will be
|
|
/// completed immediately when calling the [complete] or [completeError]
|
|
/// method on a synchronous completer, which also calls any callbacks
|
|
/// registered on that future.
|
|
///
|
|
/// Completing synchronously must not break the rule that when you add a
|
|
/// callback on a future, that callback must not be called until the code
|
|
/// that added the callback has completed.
|
|
/// For that reason, a synchronous completion must only occur at the very end
|
|
/// (in "tail position") of another synchronous event,
|
|
/// because at that point, completing the future immediately is be equivalent
|
|
/// to returning to the event loop and completing the future in the next
|
|
/// microtask.
|
|
///
|
|
/// Example:
|
|
/// ```dart
|
|
/// var completer = Completer.sync();
|
|
/// // The completion is the result of the asynchronous onDone event.
|
|
/// // No other operation is performed after the completion. It is safe
|
|
/// // to use the Completer.sync constructor.
|
|
/// stream.listen(print, onDone: () { completer.complete("done"); });
|
|
/// ```
|
|
/// Bad example. Do not use this code. Only for illustrative purposes:
|
|
/// ```dart
|
|
/// var completer = Completer.sync();
|
|
/// completer.future.then((_) { bar(); });
|
|
/// // The completion is the result of the asynchronous onDone event.
|
|
/// // However, there is still code executed after the completion. This
|
|
/// // operation is *not* safe.
|
|
/// stream.listen(print, onDone: () {
|
|
/// completer.complete("done");
|
|
/// foo(); // In this case, foo() runs after bar().
|
|
/// });
|
|
/// ```
|
|
factory Completer.sync() => _SyncCompleter<T>();
|
|
|
|
/// The future that is completed by this completer.
|
|
///
|
|
/// The future that is completed when [complete] or [completeError] is called.
|
|
Future<T> get future;
|
|
|
|
/// Completes [future] with the supplied values.
|
|
///
|
|
/// The value must be either a value of type [T]
|
|
/// or a future of type `Future<T>`.
|
|
/// If the value is omitted or `null`, and `T` is not nullable, the call
|
|
/// to `complete` throws.
|
|
///
|
|
/// If the value is itself a future, the completer will wait for that future
|
|
/// to complete, and complete with the same result, whether it is a success
|
|
/// or an error.
|
|
///
|
|
/// Calling [complete] or [completeError] must be done at most once.
|
|
///
|
|
/// All listeners on the future are informed about the value.
|
|
void complete([FutureOr<T>? value]);
|
|
|
|
/// Complete [future] with an error.
|
|
///
|
|
/// Calling [complete] or [completeError] must be done at most once.
|
|
///
|
|
/// Completing a future with an error indicates that an exception was thrown
|
|
/// while trying to produce a value.
|
|
///
|
|
/// If [error] is a [Future], the future itself is used as the error value.
|
|
/// If you want to complete with the result of the future, you can use:
|
|
/// ```dart
|
|
/// thisCompleter.complete(theFuture)
|
|
/// ```
|
|
/// or if you only want to handle an error from the future:
|
|
/// ```dart
|
|
/// theFuture.catchError(thisCompleter.completeError);
|
|
/// ```
|
|
///
|
|
/// The [future] must have an error handler installed before the call to
|
|
/// [completeError]) or [error] will be considered an uncaught error.
|
|
///
|
|
/// ```dart
|
|
/// void doStuff() {
|
|
/// // Outputs a message like:
|
|
/// // Uncaught Error: Assertion failed: "future not consumed"
|
|
/// Completer().completeError(AssertionError('future not consumed'));
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// You can install an error handler through [Future.catchError],
|
|
/// [Future.then] or the `await` operation.
|
|
///
|
|
/// ```dart
|
|
/// void doStuff() {
|
|
/// final c = Completer();
|
|
/// c.future.catchError((e) {
|
|
/// // Handle the error.
|
|
/// });
|
|
/// c.completeError(AssertionError('future not consumed'));
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// See the
|
|
/// [Zones article](https://dart.dev/articles/archive/zones#handling-uncaught-errors)
|
|
/// for details on uncaught errors.
|
|
void completeError(Object error, [StackTrace? stackTrace]);
|
|
|
|
/// Whether the [future] has been completed.
|
|
///
|
|
/// Reflects whether [complete] or [completeError] has been called.
|
|
/// A `true` value doesn't necessarily mean that listeners of this future
|
|
/// have been invoked yet, either because the completer usually waits until
|
|
/// a later microtask to propagate the result, or because [complete]
|
|
/// was called with a future that hasn't completed yet.
|
|
///
|
|
/// When this value is `true`, [complete] and [completeError] must not be
|
|
/// called again.
|
|
bool get isCompleted;
|
|
}
|
|
|
|
// Helper function completing a _Future with an error, but checking the zone
|
|
// for error replacement and missing stack trace first.
|
|
// Only used for errors that are *caught*.
|
|
// A user provided error object should use `_interceptUserError` which
|
|
// also sets `Error.stackTrace`.
|
|
void _completeWithErrorCallback(
|
|
_Future result,
|
|
Object error,
|
|
StackTrace stackTrace,
|
|
) {
|
|
result._completeErrorObject(_interceptCaughtError(error, stackTrace));
|
|
}
|