// 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` 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.then` function takes a callback [f] that returns either /// // an `S` or a `Future`. /// Future then(FutureOr f(T x), ...); /// /// // `Completer.complete` takes either a `T` or `Future`. /// void complete(FutureOr value); /// ``` /// /// ### Advanced /// /// The `FutureOr` type is actually the "type union" of the types `int` and /// `Future`. This type union is defined in such a way that /// `FutureOr` 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` is equivalent to `Object`. /// /// As a corollary, `FutureOr` is equivalent to /// `FutureOr>`, `FutureOr>` is equivalent to /// `Future`. @pragma("vm:entry-point") abstract class FutureOr { // 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 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`. /// 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` 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 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 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 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 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 { /// A `Future` completed with `null`. /// /// Currently shared with `dart:internal`. /// If that future can be removed, then change this back to /// `_Future.zoneValue(null, _rootZone);` static final _Future _nullFuture = nullFuture as _Future; /// A `Future` completed with `false`. static final _Future _falseFuture = _Future.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 computation()) { _Future result = _Future(); Timer.run(() { FutureOr 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 computation()) { _Future result = _Future(); scheduleMicrotask(() { FutureOr 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`, /// 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`, that future is returned. /// /// If calling [computation] returns a non-`Future` value, /// a future is created which has been completed with that value. /// /// Example: /// ```dart /// Future asyncAdd( /// FutureOr Function() a, /// FutureOr 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 computation()) { FutureOr result; try { result = computation(); } catch (error, stackTrace) { return _Future() .._asyncCompleteErrorObject(_interceptCaughtError(error, stackTrace)); } return result is Future ? result : _Future.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().._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.sync(() => value)`. /// /// If [value] is omitted or `null`, it is converted to `FutureOr` by /// `value as FutureOr`. 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 getFuture() { /// return Future.value(2021); /// } /// /// final result = await getFuture(); /// ``` @pragma("vm:entry-point") @pragma("vm:prefer-inline") factory Future.value([FutureOr? value]) { return _Future.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 getFuture() { /// return Future.error(Exception('Issue')); /// } /// /// final error = await getFuture(); // Throws. /// ``` factory Future.error(Object error, [StackTrace? stackTrace]) => _Future.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. /// /// 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. /// /// Example: /// ```dart /// Future.delayed(const Duration(seconds: 1), () { /// print('One second has passed.'); // Prints after 1 second. /// }); /// ``` factory Future.delayed(Duration duration, [FutureOr computation()?]) { if (computation == null && !typeAcceptsNull()) { throw ArgumentError.value( null, "computation", "The type parameter is not nullable", ); } _Future result = _Future(); Timer(duration, () { if (computation == null) { result._complete(null as T); } else { FutureOr computationResult; try { computationResult = computation(); } catch (e, s) { _completeWithErrorCallback(result, e, s); return; } result._complete(computationResult); } }); 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 delayedNumber() async { /// await Future.delayed(const Duration(seconds: 2)); /// return 2; /// } /// /// Future delayedString() async { /// await Future.delayed(const Duration(seconds: 2)); /// return 'result'; /// } /// ``` static Future> wait( Iterable> futures, { bool eagerError = false, void cleanUp(T successValue)?, }) { @pragma('vm:awaiter-link') final _Future> _future = _Future>(); List? 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? 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? 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([]); } values = List.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 slowInt() async { /// await Future.delayed(const Duration(seconds: 2)); /// return 2; /// } /// /// Future delayedString() async { /// await Future.delayed(const Duration(seconds: 2)); /// throw TimeoutException('Time has passed'); /// } /// /// Future fastInt() async { /// await Future.delayed(const Duration(seconds: 1)); /// return 3; /// } /// ``` static Future any(Iterable> futures) { var completer = Completer.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`, 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( Iterable 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` 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`, 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 doWhile(FutureOr action()) { @pragma('vm:awaiter-link') _Future doneSignal = _Future(); 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 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) { 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`. /// /// 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 then(FutureOr 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 // - (dynamic, StackTrace) -> FutureOr // 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 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 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 waitTask() { /// Future.delayed(const Duration(seconds: 5)); /// return Future.value('done'); /// } /// // Outputs: 'do some work here' after waitTask is completed. /// ``` Future whenComplete(FutureOr 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 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`. /// 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 waitTask(String string) async { /// await Future.delayed(const Duration(seconds: 5)); /// return string; /// } /// /// /// Prints "printed" after five seconds. /// Future waitPrint() async { /// await Future.delayed(const Duration(seconds: 5)); /// print("printed"); /// } /// /// Throws a [StateError] with [message] after five seconds. /// Future waitThrow(String message) async { /// await Future.delayed(const Duration(seconds: 5)); /// throw Exception(message); /// } /// ``` Future timeout(Duration timeLimit, {FutureOr 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? future) {} /// Convenience methods on futures. /// /// Adds functionality to futures which makes it easier to /// write well-typed asynchronous code. extension FutureExtensions on Future { /// 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 retryOperation(Future operation(), T onFailure()) => /// operation().onError((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((e, _) { /// throw e.cause ?? e; /// }); /// // vs. /// someFuture.onError((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 onError( FutureOr handleError(E error, StackTrace stackTrace), { bool test(E error)?, }) { FutureOr 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) { // Internal method working like `catchError`, // but allows specifying a different result future type. return unsafeCast<_Future>(this)._safeOnError(onError); } return this.then((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) { self._ignore(); } else { self.then(_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 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 { /// 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(); /// 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(); /// The future that is completed by this completer. /// /// The future that is completed when [complete] or [completeError] is called. Future get future; /// Completes [future] with the supplied values. /// /// The value must be either a value of type [T] /// or a future of type `Future`. /// 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? 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)); }