| // 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. 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 new UnsupportedError("FutureOr can't be instantiated"); |
| } |
| } |
| |
| /// An object representing a delayed 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. |
| /// 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. |
| /// |
| /// 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 |
| /// (for example [Completer.complete] or [Future.value]), |
| /// then it also accepts another future and does this work for the developer. |
| /// |
| /// 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. |
| /// 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 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. |
| /// 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 also 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> future = Future(foo); // Result of foo() as a future. |
| /// future.then((int value) => bar(value)) |
| /// .catchError((e) => 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. |
| /// |
| /// A future may also fail to ever complete. In that case, no callbacks are |
| /// called. |
| abstract class Future<T> { |
| /// A `Future<Null>` completed with `null`. |
| /// |
| /// Currently shared with `dart:internal`. |
| /// If that future can be removed, then change this back to |
| /// `_Future<Null>.zoneValue(null, _rootZone);` |
| static final _Future<Null> _nullFuture = nullFuture as _Future<Null>; |
| |
| /// A `Future<bool>` completed with `false`. |
| static final _Future<bool> _falseFuture = |
| new _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 = new _Future<T>(); |
| Timer.run(() { |
| try { |
| result._complete(computation()); |
| } catch (e, s) { |
| _completeWithErrorCallback(result, e, s); |
| } |
| }); |
| 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 = new _Future<T>(); |
| scheduleMicrotask(() { |
| try { |
| result._complete(computation()); |
| } catch (e, s) { |
| _completeWithErrorCallback(result, e, s); |
| } |
| }); |
| return result; |
| } |
| |
| /// Returns a future containing the result of immediately calling |
| /// [computation]. |
| /// |
| /// 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 value, |
| /// a future is returned which has been completed with that value. |
| factory Future.sync(FutureOr<T> computation()) { |
| try { |
| var result = computation(); |
| if (result is Future<T>) { |
| return result; |
| } else { |
| // TODO(40014): Remove cast when type promotion works. |
| return new _Future<T>.value(result as dynamic); |
| } |
| } catch (error, stackTrace) { |
| var future = new _Future<T>(); |
| AsyncError? replacement = Zone.current.errorCallback(error, stackTrace); |
| if (replacement != null) { |
| future._asyncCompleteError(replacement.error, replacement.stackTrace); |
| } else { |
| future._asyncCompleteError(error, stackTrace); |
| } |
| return future; |
| } |
| } |
| |
| /// 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. |
| @pragma("vm:entry-point") |
| @pragma("vm:prefer-inline") |
| factory Future.value([FutureOr<T>? value]) { |
| return new _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. |
| factory Future.error(Object error, [StackTrace? stackTrace]) { |
| // TODO(40614): Remove once non-nullability is sound. |
| checkNotNullable(error, "error"); |
| if (!identical(Zone.current, _rootZone)) { |
| AsyncError? replacement = Zone.current.errorCallback(error, stackTrace); |
| if (replacement != null) { |
| error = replacement.error; |
| stackTrace = replacement.stackTrace; |
| } |
| } |
| stackTrace ??= AsyncError.defaultStackTrace(error); |
| return new _Future<T>.immediateError(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. |
| /// |
| /// If the duration is 0 or less, |
| /// it completes no sooner than in the next event-loop iteration, |
| /// after all microtasks have run. |
| /// |
| /// If [computation] is omitted, |
| /// it will be treated as if [computation] was `() => null`, |
| /// and the future will eventually complete with the `null` value. |
| /// In that case, [T] must be nullable. |
| /// |
| /// 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. |
| factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) { |
| if (computation == null && !typeAcceptsNull<T>()) { |
| throw ArgumentError.value( |
| null, "computation", "The type parameter is not nullable"); |
| } |
| _Future<T> result = new _Future<T>(); |
| new Timer(duration, () { |
| if (computation == null) { |
| result._complete(null as T); |
| } else { |
| try { |
| result._complete(computation()); |
| } catch (e, s) { |
| _completeWithErrorCallback(result, e, s); |
| } |
| } |
| }); |
| 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. |
| @pragma("vm:recognized", "other") |
| static Future<List<T>> wait<T>(Iterable<Future<T>> futures, |
| {bool eagerError = false, void cleanUp(T successValue)?}) { |
| // This is a VM recognised method, and the _future variable is deliberately |
| // allocated in a specific slot in the closure context for stack unwinding. |
| 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. |
| late Object error; // The first error from a future. |
| late StackTrace stackTrace; // The stackTrace that came with the error. |
| |
| // Handle an error from any of the futures. |
| void handleError(Object theError, StackTrace theStackTrace) { |
| remaining--; |
| List<T?>? valueList = values; |
| if (valueList != null) { |
| if (cleanUp != null) { |
| for (var value in valueList) { |
| if (value != null) { |
| // Ensure errors from cleanUp are uncaught. |
| T cleanUpValue = value; |
| new Future.sync(() { |
| cleanUp(cleanUpValue); |
| }); |
| } |
| } |
| } |
| values = null; |
| if (remaining == 0 || eagerError) { |
| _future._completeError(theError, theStackTrace); |
| } else { |
| error = theError; |
| stackTrace = theStackTrace; |
| } |
| } else if (remaining == 0 && !eagerError) { |
| _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) { |
| remaining--; |
| List<T?>? valueList = values; |
| if (valueList != null) { |
| valueList[pos] = value; |
| if (remaining == 0) { |
| _future._completeWithValue(List<T>.from(valueList)); |
| } |
| } else { |
| if (cleanUp != null && value != null) { |
| // Ensure errors from cleanUp are uncaught. |
| new Future.sync(() { |
| cleanUp(value); |
| }); |
| } |
| if (remaining == 0 && !eagerError) { |
| // If eagerError is false, and valueList is null, then |
| // error and stackTrace have been set in handleError above. |
| _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) { |
| return _future.._completeWithValue(<T>[]); |
| } |
| values = new List<T?>.filled(remaining, null); |
| } catch (e, st) { |
| // 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) { |
| // Throw a new Future.error. |
| // Don't just call `_future._completeError` since that would propagate |
| // the error too eagerly, not giving the callers time to install |
| // error handlers. |
| // Also, don't use `_asyncCompleteError` since that one doesn't give |
| // zones the chance to intercept the error. |
| return new Future.error(e, st); |
| } else { |
| // Don't allocate a list for values, thus indicating that there was an |
| // error. |
| // Set error to the caught exception. |
| error = e; |
| stackTrace = st; |
| } |
| } |
| 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. |
| static Future<T> any<T>(Iterable<Future<T>> futures) { |
| var completer = new 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 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. |
| static Future doWhile(FutureOr<bool> action()) { |
| _Future<void> doneSignal = new _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. |
| _asyncCompleteWithErrorCallback(doneSignal, error, stackTrace); |
| return; |
| } |
| if (result is Future<bool>) { |
| result.then(nextIteration, onError: doneSignal._completeError); |
| return; |
| } |
| // TODO(40014): Remove cast when type promotion works. |
| keepGoing = result as bool; |
| } |
| 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`, |
| /// the throw is considered a rethrow, |
| /// and the original stack trace is used instead. |
| /// |
| /// 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]. |
| // 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 |
| // `isCheck` 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; |
| /// }); |
| /// } |
| /// ``` |
| 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(); |
| |
| /// Time-out the future computation after [timeLimit] has passed. |
| /// |
| /// Returns a new future that completes with the same value as this future, |
| /// if this future completes in time. |
| /// |
| /// If this future does not complete before `timeLimit` has passed, |
| /// the [onTimeout] action is executed instead, and its result (whether it |
| /// returns or throws) is used as the result of the returned future. |
| /// The [onTimeout] function must return a [T] or a `Future<T>`. |
| /// |
| /// If `onTimeout` is omitted, a timeout will cause the returned future to |
| /// complete with a [TimeoutException]. |
| Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()?}); |
| } |
| |
| /// Convenience methods on futures. |
| /// |
| /// Adds functionality to futures which makes it easier to |
| /// write well-typed asynchronous code. |
| @Since("2.12") |
| 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].toString() |
| /// |
| /// 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 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)?}) { |
| // There are various ways to optimize this to avoid the double is E/as E |
| // type check, but for now we are not optimizing the error path. |
| return this.catchError( |
| (Object error, StackTrace stackTrace) => |
| handleError(error as E, stackTrace), |
| test: (Object error) => error is E && (test == null || test(error))); |
| } |
| } |
| |
| /// 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); |
| /// } |
| /// } |
| /// ``` |
| abstract 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() => new _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() => new _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); |
| /// ``` |
| 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 error, but checking the zone |
| // for error replacement and missing stack trace first. |
| void _completeWithErrorCallback( |
| _Future result, Object error, StackTrace? stackTrace) { |
| AsyncError? replacement = Zone.current.errorCallback(error, stackTrace); |
| if (replacement != null) { |
| error = replacement.error; |
| stackTrace = replacement.stackTrace; |
| } else { |
| stackTrace ??= AsyncError.defaultStackTrace(error); |
| } |
| if (stackTrace == null) throw "unreachable"; // TODO(40088). |
| result._completeError(error, stackTrace); |
| } |
| |
| // Like [_completeWithErrorCallback] but completes asynchronously. |
| void _asyncCompleteWithErrorCallback( |
| _Future result, Object error, StackTrace? stackTrace) { |
| AsyncError? replacement = Zone.current.errorCallback(error, stackTrace); |
| if (replacement != null) { |
| error = replacement.error; |
| stackTrace = replacement.stackTrace; |
| } else { |
| stackTrace ??= AsyncError.defaultStackTrace(error); |
| } |
| if (stackTrace == null) { |
| throw "unreachable"; // TODO(lrn): Remove when type promotion works. |
| } |
| result._asyncCompleteError(error, stackTrace); |
| } |