// 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"; /// Sets [stackTrace] as [Error.stackTrace] on [error], if that is an [Error]. /// /// If [error] is not an instance of a class which extends [Error], /// or if it already has an [Error.stackTrace] set, nothing happens. @pragma("wasm:entry-point") external void _trySetStackTrace(Object error, StackTrace stackTrace); /// Calls the [Zone.errorCallback] of the current zone with the error and stack. /// /// If a non-`null` result is returned, it tries to set the stack trace /// on the returned error to the returned stack trace. AsyncError? _interceptError(Object error, StackTrace? stackTrace) { var zone = Zone._current; if (identical(zone, _rootZone)) return null; var replacement = zone.errorCallback(error, stackTrace); if (replacement == null) return null; _trySetStackTrace(replacement.error, replacement.stackTrace); return replacement; } /// As [_interceptError], but an `AsyncError` of the error if not replaced. /// /// Also like [_interceptUserError], but for errors that are already /// asynchronous. AsyncError _interceptCaughtError(Object error, StackTrace? stackTrace) => _interceptError(error, stackTrace) ?? AsyncError(error, stackTrace); /// Used for user-provided error and stack trace that are to become async /// errors. /// /// Allows `Zone.current.errorCallback` to intercept and modify the error, /// and sets the stack trace on the error as if it was thrown. /// /// Used for errors given to async functions like [Completer.completeError] /// or [StreamController.addError]. /// /// For errors that are caught (and have therefore been thrown already) /// use [_interceptCaughtError], which doesn't try to set a stack trace on the /// error object. /// /// Errors that are already asynchronous (coming out of streams or futures) /// should not call [Zone.errorCallback]. AsyncError _interceptUserError(Object error, StackTrace? stackTrace) { var zone = Zone.current; if (!identical(zone, _rootZone)) { var replacement = _interceptError(error, stackTrace); if (replacement != null) return replacement; } if (stackTrace == null) { // Inlines `AsyncError.defaultStackTrace`, to reuse the `is Error` check // result to also not do `_trySetStackTrace` on known non-`Error`s. if (error is Error) { stackTrace = error.stackTrace; if (stackTrace == null) { stackTrace = StackTrace.empty; _trySetStackTrace(error, stackTrace); } } else { stackTrace = StackTrace.empty; } } else { _trySetStackTrace(error, stackTrace); // Maybe it's an Error. } return AsyncError._(error, stackTrace); } abstract class _Completer implements Completer { @pragma("vm:entry-point") final _Future future = _Future(); // Overridden by either a synchronous or asynchronous implementation. void complete([FutureOr? value]); void completeError(Object error, [StackTrace? stackTrace]) { if (!future._mayComplete) throw StateError("Future already completed"); _completeErrorObject(_interceptUserError(error, stackTrace)); } // Overridden by either a synchronous or asynchronous implementation. void _completeErrorObject(AsyncError error); // The future's _isComplete doesn't take into account pending completions. // We therefore use _mayComplete. bool get isCompleted => !future._mayComplete; } /// Completer which completes future asynchronously. @pragma("vm:entry-point") class _AsyncCompleter extends _Completer { void complete([FutureOr? value]) { if (!future._mayComplete) throw StateError("Future already completed"); future._asyncComplete(value == null ? value as dynamic : value); } void _completeErrorObject(AsyncError error) { future._asyncCompleteErrorObject(error); } } /// Completer which completes future synchronously. /// /// Created by [Completer.sync]. Use with caution. @pragma("vm:entry-point") class _SyncCompleter extends _Completer { void complete([FutureOr? value]) { if (!future._mayComplete) throw StateError("Future already completed"); future._complete(value == null ? value as dynamic : value); } void _completeErrorObject(AsyncError error) { future._completeErrorObject(error); } } class _FutureListener { // Keep in sync with sdk/runtime/vm/stack_trace.cc. static const int maskValue = 1 << 0; static const int maskError = 1 << 1; static const int maskTestError = 1 << 2; static const int maskWhenComplete = 1 << 3; static const int maskAwait = 1 << 4; static const int stateChain = 0; // Handles values, passes errors on. static const int stateThen = maskValue; // Handles values and errors. static const int stateThenOnerror = maskValue | maskError; // Handles values and error. Created by the implementation of `await`. static const int stateThenAwait = stateThenOnerror | maskAwait; // Handles errors, has errorCallback. static const int stateCatchError = maskError; // Ignores both values and errors. Has no callback or errorCallback. // The [result] future is ignored, its always the same as the source. static const int stateCatchErrorTest = maskError | maskTestError; static const int stateWhenComplete = maskWhenComplete; static const int maskType = maskValue | maskError | maskTestError | maskWhenComplete; // Listeners on the same future are linked through this link. @pragma("vm:entry-point") _FutureListener? _nextListener; // The future to complete when this listener is activated. @pragma("vm:entry-point") final _Future result; // Which fields means what. @pragma("vm:entry-point") final int state; // Used for then/whenDone callback and error test @pragma("vm:entry-point") final Function? callback; // Used for error callbacks. final Function? errorCallback; _FutureListener.then( this.result, FutureOr Function(S) onValue, Function? errorCallback, ) : callback = onValue, errorCallback = errorCallback, state = (errorCallback == null) ? stateThen : stateThenOnerror; _FutureListener.thenAwait( this.result, FutureOr Function(S) onValue, Function errorCallback, ) : callback = onValue, errorCallback = errorCallback, state = stateThenAwait; _FutureListener.catchError(this.result, this.errorCallback, this.callback) : state = (callback == null) ? stateCatchError : stateCatchErrorTest; _FutureListener.whenComplete(this.result, this.callback) : errorCallback = null, state = stateWhenComplete; _Zone get _zone => result._zone; bool get handlesValue => (state & maskValue != 0); bool get handlesError => (state & maskError != 0); bool get hasErrorTest => (state & maskType == stateCatchErrorTest); bool get handlesComplete => (state & maskType == stateWhenComplete); FutureOr Function(S) get _onValue { assert(handlesValue); return unsafeCast Function(S)>(callback); } Function? get _onError => errorCallback; bool Function(Object) get _errorTest { assert(hasErrorTest); return unsafeCast(callback); } dynamic Function() get _whenCompleteAction { assert(handlesComplete); return unsafeCast(callback); } /// Whether this listener has an error callback. /// /// This function must only be called if the listener [handlesError]. bool get hasErrorCallback { assert(handlesError); return _onError != null; } @pragma("vm:recognized", "other") @pragma("vm:never-inline") @pragma("vm:invisible") FutureOr handleValue(S sourceResult) { return _zone.runUnary, S>(_onValue, sourceResult); } bool matchesErrorTest(AsyncError asyncError) { if (!hasErrorTest) return true; return _zone.runUnary(_errorTest, asyncError.error); } FutureOr handleError(AsyncError asyncError) { assert(handlesError && hasErrorCallback); var errorCallback = this.errorCallback; // To enable promotion. // If the errorCallback returns something which is not a FutureOr, // this return statement throws, and the caller handles the error. dynamic result; if (errorCallback is dynamic Function(Object, StackTrace)) { result = _zone.runBinary( errorCallback, asyncError.error, asyncError.stackTrace, ); } else { result = _zone.runUnary( errorCallback as dynamic, asyncError.error, ); } // Give better error messages if the result is not a valid // FutureOr. try { return result; } on TypeError { if (handlesValue) { // This is a `.then` callback with an `onError`. throw ArgumentError( "The error handler of Future.then" " must return a value of the returned future's type", "onError", ); } // This is a `catchError` callback. throw ArgumentError( "The error handler of " "Future.catchError must return a value of the future's type", "onError", ); } } dynamic handleWhenComplete() { assert(!handlesError); return _zone.run(_whenCompleteAction); } // Whether the [value] future should be awaited and the [future] completed // with its result, rather than just completing the [future] directly // with the [value]. bool shouldChain(Future value) => value is Future || value is! T; } class _Future implements Future { /// Initial state, waiting for a result. In this state, the /// [_resultOrListeners] field holds a single-linked list of /// [_FutureListener] listeners. static const int _stateIncomplete = 0; /// Flag set when an error need not be handled. /// /// Set by the [FutureExtensions.ignore] method to avoid /// having to introduce an unnecessary listener. /// Only relevant until the future is completed. /// /// When changing update runtime/vm/stack_trace.cc static const int _stateIgnoreError = 1; /// Pending completion. Set when completed using [_asyncComplete] or /// [_asyncCompleteError]. It is an error to try to complete it again. /// [_resultOrListeners] holds listeners. static const int _statePendingComplete = 2; /// The future has been chained to another "source" [_Future]. /// /// The result of that other future becomes the result of this future /// as well, when the other future completes. /// This future cannot be completed again. /// [_resultOrListeners] contains the source future. /// Listeners have been moved to the chained future. static const int _stateChained = 4; /// The future has been completed with a value result. /// /// [_resultOrListeners] contains the value. static const int _stateValue = 8; /// The future has been completed with an error result. /// /// [_resultOrListeners] contains an [AsyncError] /// holding the error and stack trace. static const int _stateError = 16; /// Mask for the states above except [_stateIgnoreError]. static const int _completionStateMask = 30; /// Whether the future is complete, and as what. @pragma('vm:entry-point') int _state = _stateIncomplete; /// Zone that the future was completed from. /// This is the zone that an error result belongs to. /// /// Until the future is completed, the field may hold the zone that /// listener callbacks used to create this future should be run in. final _Zone _zone; /// Either the result, a list of listeners or another future. /// /// The result of the future is either a value or an error. /// A result is only stored when the future has completed. /// /// The listeners is an internally linked list of [_FutureListener]s. /// Listeners are only remembered while the future is not yet complete, /// and it is not chained to another future. /// /// The future is another future that this future is chained to. This future /// is waiting for the other future to complete, and when it does, /// this future will complete with the same result. /// All listeners are forwarded to the other future. @pragma("vm:entry-point") var _resultOrListeners; // This constructor is used by async/await. _Future() : _zone = Zone._current; // Empty `_Future` in a given zone. _Future.zone(this._zone); // Constructor used by [Future.value]. _Future.immediate(FutureOr result) : _zone = Zone._current { _asyncComplete(result); } /// Creates a future with the value and the specified zone. _Future.zoneValue(T value, this._zone) { _setValue(value); } _Future.immediateError(AsyncError error) : _zone = Zone._current { _asyncCompleteErrorObject(error); } /// Creates a future that is already completed with the value. _Future.value(T value) : this.zoneValue(value, Zone._current); bool get _mayComplete => (_state & _completionStateMask) == _stateIncomplete; bool get _isPendingComplete => (_state & _statePendingComplete) != 0; bool get _mayAddListener => _state <= (_statePendingComplete | _stateIgnoreError); bool get _isChained => (_state & _stateChained) != 0; bool get _isComplete => (_state & (_stateValue | _stateError)) != 0; bool get _hasError => (_state & _stateError) != 0; bool get _ignoreError => (_state & _stateIgnoreError) != 0; void _setChained(_Future source) { assert(_mayAddListener); _state = _stateChained | (_state & _stateIgnoreError); _resultOrListeners = source; } Future then(FutureOr f(T value), {Function? onError}) { Zone currentZone = Zone.current; if (identical(currentZone, _rootZone)) { if (onError != null && onError is! Function(Object, StackTrace) && onError is! Function(Object)) { throw ArgumentError.value( onError, "onError", "Error handler must accept one Object or one Object and a StackTrace" " as arguments, and return a value of the returned future's type", ); } } else { f = currentZone.registerUnaryCallback, T>(f); if (onError != null) { // This call also checks that onError is assignable to one of: // dynamic Function(Object) // dynamic Function(Object, StackTrace) onError = _registerErrorHandler(onError, currentZone); } } _Future result = _Future(); _addListener(_FutureListener.then(result, f, onError)); return result; } /// Registers a system created result and error continuation. /// /// Used by the implementation of `await` to listen to a future. /// The system created listeners are not registered in the zone. Future _thenAwait(FutureOr f(T value), Function onError) { _Future result = _Future(); _addListener(_FutureListener.thenAwait(result, f, onError)); return result; } void _ignore() { _state |= _stateIgnoreError; if (_isChained) { // Mark chain source. If it has no listeners at all, then neither // does this future. (But this may suppress errors on other futures // with the same chain source and no listeners, which haven't been // ignored.) _Future source = this; do { source = source._chainSource; } while (source._isChained); source._state |= _stateIgnoreError; } } Future catchError(Function onError, {bool test(Object error)?}) { _Future result = _Future(); if (!identical(result._zone, _rootZone)) { onError = _registerErrorHandler(onError, result._zone); if (test != null) test = result._zone.registerUnaryCallback(test); } _addListener(_FutureListener.catchError(result, onError, test)); return result; } // Used by extension method `onError` to up-cast the result to `R`. // // Not public because we cannot statically ensure that [R] is a supertype // of [T]. // // Avoids needing to allocate an extra value handler to do the up cast, // which is needed if using `.then`. Future _safeOnError(FutureOr Function(Object, StackTrace) onError) { assert(this is _Future); // Is up-cast. _Future result = _Future(); if (!identical(result._zone, _rootZone)) { onError = result._zone.registerBinaryCallback(onError); } _addListener(_FutureListener.catchError(result, onError, null)); return result; } Future whenComplete(dynamic action()) { _Future result = _Future(); if (!identical(result._zone, _rootZone)) { action = result._zone.registerCallback(action); } _addListener(_FutureListener.whenComplete(result, action)); return result; } Stream asStream() => Stream.fromFuture(this); void _setPendingComplete() { assert(_mayComplete); // Aka. _stateIncomplete _state ^= _stateIncomplete ^ _statePendingComplete; } void _clearPendingComplete() { assert(_isPendingComplete); _state ^= _statePendingComplete ^ _stateIncomplete; } AsyncError get _error { assert(_hasError); return _resultOrListeners; } _Future get _chainSource { assert(_isChained); return _resultOrListeners; } // This method is used by async/await. void _setValue(T value) { assert(!_isComplete); // But may have a completion pending. _state = _stateValue; _resultOrListeners = value; } void _setErrorObject(AsyncError error) { assert(!_isComplete); // But may have a completion pending. _state = _stateError | (_state & _stateIgnoreError); _resultOrListeners = error; } /// Copy the completion result of [source] into this future. /// /// Used when a chained future notices that its source is completed. void _cloneResult(_Future source) { assert(!_isComplete); assert(source._isComplete); _state = (source._state & _completionStateMask) | (_state & _stateIgnoreError); _resultOrListeners = source._resultOrListeners; } void _addListener(_FutureListener listener) { assert(listener._nextListener == null); if (_mayAddListener) { listener._nextListener = _resultOrListeners; _resultOrListeners = listener; } else { if (_isChained) { // Delegate listeners to chained source future. // If the source is complete, instead copy its values and // drop the chaining. _Future source = _chainSource; if (!source._isComplete) { source._addListener(listener); return; } _cloneResult(source); } assert(_isComplete); // Handle late listeners asynchronously. _zone.scheduleMicrotask(() { _propagateToListeners(this, listener); }); } } void _prependListeners(_FutureListener? listeners) { if (listeners == null) return; if (_mayAddListener) { _FutureListener? existingListeners = _resultOrListeners; _resultOrListeners = listeners; if (existingListeners != null) { _FutureListener cursor = listeners; _FutureListener? next = cursor._nextListener; while (next != null) { cursor = next; next = cursor._nextListener; } cursor._nextListener = existingListeners; } } else { if (_isChained) { // Delegate listeners to chained source future. // If the source is complete, instead copy its values and // drop the chaining. _Future source = _chainSource; if (!source._isComplete) { source._prependListeners(listeners); return; } _cloneResult(source); } assert(_isComplete); listeners = _reverseListeners(listeners); _zone.scheduleMicrotask(() { _propagateToListeners(this, listeners); }); } } _FutureListener? _removeListeners() { // Reverse listeners before returning them, so the resulting list is in // subscription order. assert(!_isComplete); _FutureListener? current = _resultOrListeners; _resultOrListeners = null; return _reverseListeners(current); } _FutureListener? _reverseListeners(_FutureListener? listeners) { _FutureListener? prev = null; _FutureListener? current = listeners; while (current != null) { _FutureListener? next = current._nextListener; current._nextListener = prev; prev = current; current = next; } return prev; } /// Completes this future with the result of [source]. /// /// The [source] future should not be a [_Future], use /// [_chainCoreFuture] for those. /// /// Since [source] is an unknown [Future], it's interacted with /// through [Future.then], which is required to be asynchronous. void _chainForeignFuture(Future source) { assert(!_isComplete); assert(source is! _Future); // Mark the target as chained (and as such half-completed). _setPendingComplete(); try { source.then( (value) { assert(_isPendingComplete); _clearPendingComplete(); // Clear this first, it's set again. try { _completeWithValue(value as T); } catch (error, stackTrace) { _completeError(error, stackTrace); } }, onError: (Object error, StackTrace stackTrace) { assert(_isPendingComplete); _completeError(error, stackTrace); }, ); } catch (e, s) { // This only happens if the `then` call threw synchronously when given // valid arguments. // That requires a non-conforming implementation of the Future interface, // which should, hopefully, never happen. scheduleMicrotask(() { _completeError(e, s); }); } } /// Synchronously completes a target future with another, source, future. /// /// If the source future is already completed, its result is synchronously /// propagated to the target future's listeners. /// If the source future is not completed, the target future is made /// to listen for its completion. static void _chainCoreFuture(_Future source, _Future target, bool sync) { assert(target._mayAddListener); // Not completed, not already chained. while (source._isChained) { source = source._chainSource; } if (identical(source, target)) { target._asyncCompleteError( ArgumentError.value( source, null, "Cannot complete a future with itself", ), StackTrace.current, ); return; } var ignoreError = target._state & _stateIgnoreError; source._state |= ignoreError; if (!source._isComplete) { // Chain immediately if the source is not complete. // This won't call any listeners, whether completing sync or async. _FutureListener? listeners = target._resultOrListeners; target._setChained(source); source._prependListeners(listeners); return; } if (sync || (target._resultOrListeners == null && (!source._hasError || ignoreError != 0))) { // Complete synchronously when allowed. // If no-one is listening this won't call any listeners synchronously. // Do delay un-ignored errors to give time to add listeners. _FutureListener? listeners = target._removeListeners(); target._cloneResult(source); _propagateToListeners(target, listeners); return; } // Otherwise delay the chaining to avoid any synchronous callbacks. target._setPendingComplete(); target._zone.scheduleMicrotask(() { _chainCoreFuture(source, target, _allowCompleteSync); }); } /// Synchronously completes this future with [value]. /// /// If [value] is a value or an already completed [_Future], /// the result is immediately used to complete this future. /// If [value] is an incomplete future, this future will wait for /// it to complete, then use the result. void _complete(FutureOr value) { assert(!_isComplete); if (value is Future) { if (value is _Future) { _chainCoreFuture(value, this, _allowCompleteSync); } else { _chainForeignFuture(value); } } else { _FutureListener? listeners = _removeListeners(); _setValue(value); _propagateToListeners(this, listeners); } } void _completeWithValue(T value) { assert(!_isComplete); _FutureListener? listeners = _removeListeners(); _setValue(value); _propagateToListeners(this, listeners); } void _completeWithResultOf(_Future source) { assert(source._isComplete); if (source._hasError && !_zone.inSameErrorZone(source._zone)) { return; } _FutureListener? listeners = _removeListeners(); _cloneResult(source); _propagateToListeners(this, listeners); } void _completeErrorObject(AsyncError error) { assert(!_isComplete); _FutureListener? listeners = _removeListeners(); _setErrorObject(error); _propagateToListeners(this, listeners); } void _completeError(Object error, StackTrace stackTrace) { _completeErrorObject(AsyncError(error, stackTrace)); } // Completes future in a later microtask. void _asyncComplete(FutureOr value) { assert(!_isComplete); // Allows both pending complete and incomplete. // Two corner cases if the value is a future: // 1. the future is already completed and an error. // 2. the future is not yet completed but might become an error. // The first case means that we must not immediately complete the Future, // as our code would immediately start propagating the error without // giving the time to install error-handlers. // However the second case requires us to deal with the value immediately. // Otherwise the value could complete with an error and report an // unhandled error, even though we know we are already going to listen to // it. if (value is Future) { _chainFuture(value); return; } _asyncCompleteWithValue(value); } /// Internal helper function used by the implementation of `async` functions. /// /// Like [_asyncComplete], but avoids type checks that are guaranteed to /// succeed by the way the function is called. /// Should be used judiciously. void _asyncCompleteUnchecked(/*FutureOr*/ dynamic value) { // Ensure [value] is FutureOr, do so using an `as` check so it works // also correctly in non-sound null-safety mode. assert(identical(value as FutureOr, value)); final typedValue = unsafeCast>(value); // Doing just "is Future" is not sufficient. // If `T` is Object` and `value` is `Future.value(null)`, // then value is a `Future`, but not a `Future`, and going through the // `_chainFuture` branch would end up assigning `null` to `Object`. if (typedValue is Future) { _chainFuture(typedValue); return; } _asyncCompleteWithValue(unsafeCast(typedValue)); } /// Internal helper function used to implement `async` functions. /// /// Like [_asyncCompleteUnchecked], but avoids a `is Future` check due to /// having a static guarantee on the callsite that the [value] cannot be a /// [Future]. /// Should be used judiciously. void _asyncCompleteUncheckedNoFuture(/*T*/ dynamic value) { // Ensure [value] is T, do so using an `as` check so it works also correctly // in non-sound null-safety mode. assert(identical(value as T, value)); _asyncCompleteWithValue(unsafeCast(value)); } void _asyncCompleteWithValue(T value) { _setPendingComplete(); _zone.scheduleMicrotask(() { _completeWithValue(value); }); } /// Asynchronously completes a future with another future. /// /// Even if [value] is already completed, it won't synchronously /// complete this completer's future. void _chainFuture(Future value) { assert(_mayComplete); if (value is _Future) { // Chain ensuring that we don't complete synchronously. _chainCoreFuture(value, this, _requireCompleteAsync); return; } // Just listen on the foreign future. This guarantees an async delay. _chainForeignFuture(value); } void _asyncCompleteError(Object error, StackTrace stackTrace) { _asyncCompleteErrorObject(AsyncError(error, stackTrace)); } void _asyncCompleteErrorObject(AsyncError error) { assert(!_isComplete); _setPendingComplete(); _zone.scheduleMicrotask(() { _completeErrorObject(error); }); } /// Propagates the value/error of [source] to its [listeners], executing the /// listeners' callbacks. static void _propagateToListeners( _Future source, _FutureListener? listeners, ) { while (true) { assert(source._isComplete); bool hasError = source._hasError; if (listeners == null) { if (hasError && !source._ignoreError) { AsyncError asyncError = source._error; source._zone.handleUncaughtError( asyncError.error, asyncError.stackTrace, ); } return; } // Usually futures only have one listener. If they have several, we // call handle them separately in recursive calls, continuing // here only when there is only one listener left. _FutureListener listener = listeners; _FutureListener? nextListener = listener._nextListener; while (nextListener != null) { listener._nextListener = null; _propagateToListeners(source, listener); listener = nextListener; nextListener = listener._nextListener; } final dynamic sourceResult = source._resultOrListeners; // Do the actual propagation. // Set initial state of listenerHasError and listenerValueOrError. These // variables are updated with the outcome of potential callbacks. // Non-error results, including futures, are stored in // listenerValueOrError and listenerHasError is set to false. Errors // are stored in listenerValueOrError as an [AsyncError] and // listenerHasError is set to true. bool listenerHasError = hasError; var listenerValueOrError = sourceResult; // Only if we either have an error or callbacks, go into this, somewhat // expensive, branch. Here we'll enter/leave the zone. Many futures // don't have callbacks, so this is a significant optimization. if (hasError || listener.handlesValue || listener.handlesComplete) { _Zone zone = listener._zone; if (hasError && !source._zone.inSameErrorZone(zone)) { // Don't cross zone boundaries with errors. AsyncError asyncError = source._error; source._zone.handleUncaughtError( asyncError.error, asyncError.stackTrace, ); return; } _Zone? oldZone; if (!identical(Zone._current, zone)) { // Change zone if it's not current. oldZone = Zone._enter(zone); } // These callbacks are abstracted to isolate the try/catch blocks // from the rest of the code to work around a V8 glass jaw. void handleWhenCompleteCallback() { // The whenComplete-handler is not combined with normal value/error // handling. This means at most one handleX method is called per // listener. assert(!listener.handlesValue); assert(!listener.handlesError); var completeResult; try { completeResult = listener.handleWhenComplete(); } catch (e, s) { if (hasError && identical(source._error.error, e)) { listenerValueOrError = source._error; } else { listenerValueOrError = AsyncError(e, s); } listenerHasError = true; return; } if (completeResult is _Future && completeResult._isComplete) { if (completeResult._hasError) { listenerValueOrError = completeResult._error; listenerHasError = true; } // Otherwise use the existing result of source. return; } if (completeResult is Future) { // We have to wait for the completeResult future to complete // before knowing if it's an error or we should use the result // of source. var originalSource = source; var joinedResult = source._newFutureWithSameType(); completeResult.then( (_) { joinedResult._completeWithResultOf(originalSource); }, onError: (Object e, StackTrace s) { joinedResult._completeErrorObject(AsyncError(e, s)); }, ); listenerValueOrError = joinedResult; listenerHasError = false; } } void handleValueCallback() { try { listenerValueOrError = listener.handleValue(sourceResult); } catch (e, s) { listenerValueOrError = AsyncError(e, s); listenerHasError = true; } } void handleError() { try { AsyncError asyncError = source._error; if (listener.matchesErrorTest(asyncError) && listener.hasErrorCallback) { listenerValueOrError = listener.handleError(asyncError); listenerHasError = false; } } catch (e, s) { if (identical(source._error.error, e)) { listenerValueOrError = source._error; } else { listenerValueOrError = AsyncError(e, s); } listenerHasError = true; } } if (listener.handlesComplete) { handleWhenCompleteCallback(); } else if (!hasError) { if (listener.handlesValue) { handleValueCallback(); } } else { if (listener.handlesError) { handleError(); } } // If we changed zone, oldZone will not be null. if (oldZone != null) Zone._leave(oldZone); // If the listener's value is a future we *might* need to chain it. Note that // this can only happen if there is a callback. if (listenerValueOrError is Future && listener.shouldChain(listenerValueOrError)) { Future chainSource = listenerValueOrError; // Shortcut if the chain-source is already completed. Just continue // the loop. _Future result = listener.result; if (chainSource is _Future) { if (chainSource._isComplete) { listeners = result._removeListeners(); result._cloneResult(chainSource); source = chainSource; continue; } else { _chainCoreFuture(chainSource, result, _allowCompleteSync); } } else { result._chainForeignFuture(chainSource); } return; } } _Future result = listener.result; listeners = result._removeListeners(); if (!listenerHasError) { result._setValue(listenerValueOrError); } else { AsyncError asyncError = listenerValueOrError; result._setErrorObject(asyncError); } // Prepare for next round. source = result; } } /// New uncompleted future with same type. /// /// In same zone or other [zone] if specified. _Future _newFutureWithSameType([_Zone? zone]) => _Future.zone(zone ?? _zone); @pragma("vm:entry-point") Future timeout(Duration timeLimit, {FutureOr onTimeout()?}) { if (_isComplete) return _Future.immediate(this); @pragma('vm:awaiter-link') _Future _future = _Future(); Timer timer; if (onTimeout == null) { timer = Timer(timeLimit, () { _future._completeErrorObject( AsyncError( TimeoutException("Future not completed", timeLimit), StackTrace.current, ), ); }); } else { Zone zone = Zone.current; FutureOr Function() onTimeoutHandler = zone.registerCallback( onTimeout, ); timer = Timer(timeLimit, () { try { _future._complete(zone.run(onTimeoutHandler)); } catch (e, s) { _future._completeErrorObject(AsyncError(e, s)); } }); } this.then( (T v) { if (timer.isActive) { timer.cancel(); _future._completeWithValue(v); } }, onError: (Object e, StackTrace s) { if (timer.isActive) { timer.cancel(); _future._completeErrorObject(AsyncError(e, s)); } }, ); return _future; } } /// Registers errorHandler in zone if it has the correct type. /// /// Checks that the function accepts either an [Object] and a [StackTrace] /// or just one [Object]. Does not check the return type. /// The actually returned value must be `FutureOr` where `R` is the /// value type of the future that the call will complete (either returned /// by [Future.then] or [Future.catchError]). We check the returned value /// dynamically because the functions are passed as arguments in positions /// without inference, so a function expression won't infer the return type. /// /// Throws if the type is not valid. Function _registerErrorHandler(Function errorHandler, Zone zone) { if (errorHandler is dynamic Function(Object, StackTrace)) { return zone.registerBinaryCallback( errorHandler, ); } if (errorHandler is dynamic Function(Object)) { return zone.registerUnaryCallback(errorHandler); } throw ArgumentError.value( errorHandler, "onError", "Error handler must accept one Object or one Object and a StackTrace" " as arguments, and return a value of the returned future's type", ); } // Names for positional arguments to _chainCoreFuture. const _allowCompleteSync = true; const _requireCompleteAsync = false;