Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Unified Diff: tool/input_sdk/lib/async/future_impl.dart

Issue 1953153002: Update dart:async to match the Dart repo. (Closed) Base URL: https://github.com/dart-lang/dev_compiler.git@master
Patch Set: Remove unneeded calls. Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tool/input_sdk/lib/async/future.dart ('k') | tool/input_sdk/lib/async/schedule_microtask.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tool/input_sdk/lib/async/future_impl.dart
diff --git a/tool/input_sdk/lib/async/future_impl.dart b/tool/input_sdk/lib/async/future_impl.dart
index 1c6bf545e9e84e953f5d189001a20f04e9057e97..eb4d6cfcaaf090a9c862cdaabeac192afdff1a69 100644
--- a/tool/input_sdk/lib/async/future_impl.dart
+++ b/tool/input_sdk/lib/async/future_impl.dart
@@ -5,7 +5,7 @@
part of dart.async;
/** The onValue and onError handlers return either a value or a future */
-typedef dynamic _FutureOnValue<T>(T value);
+typedef dynamic/*T|Future<T>*/ _FutureOnValue<S, T>(S value);
/** Test used by [Future.catchError] to handle skip some errors. */
typedef bool _FutureErrorTest(var error);
/** Used by [WhenFuture]. */
@@ -57,21 +57,23 @@ class _SyncCompleter<T> extends _Completer<T> {
}
}
-class _FutureListener {
+class _FutureListener<S, T> {
static const int MASK_VALUE = 1;
static const int MASK_ERROR = 2;
static const int MASK_TEST_ERROR = 4;
static const int MASK_WHENCOMPLETE = 8;
static const int STATE_CHAIN = 0;
static const int STATE_THEN = MASK_VALUE;
- static const int STATE_THEN_ONERROR = MASK_VALUE | MASK_ERROR;
+ // TODO(johnmccutchan): Remove the hard coded value. See #26030.
+ static const int STATE_THEN_ONERROR = 3; // MASK_VALUE | MASK_ERROR.
static const int STATE_CATCHERROR = MASK_ERROR;
- static const int STATE_CATCHERROR_TEST = MASK_ERROR | MASK_TEST_ERROR;
+ // TODO(johnmccutchan): Remove the hard coded value. See #26030.
+ static const int STATE_CATCHERROR_TEST = 6; // MASK_ERROR | MASK_TEST_ERROR.
static const int STATE_WHENCOMPLETE = MASK_WHENCOMPLETE;
// Listeners on the same future are linked through this link.
_FutureListener _nextListener = null;
// The future to complete when this listener is activated.
- final _Future result;
+ final _Future<T> result;
// Which fields means what.
final int state;
// Used for then/whenDone callback and error test
@@ -80,7 +82,7 @@ class _FutureListener {
final Function errorCallback;
_FutureListener.then(this.result,
- _FutureOnValue onValue, Function errorCallback)
+ _FutureOnValue<S, T> onValue, Function errorCallback)
: callback = onValue,
errorCallback = errorCallback,
state = (errorCallback == null) ? STATE_THEN : STATE_THEN_ONERROR;
@@ -95,11 +97,6 @@ class _FutureListener {
errorCallback = null,
state = STATE_WHENCOMPLETE;
- _FutureListener.chain(this.result)
- : callback = null,
- errorCallback = null,
- state = STATE_CHAIN;
-
Zone get _zone => result._zone;
bool get handlesValue => (state & MASK_VALUE != 0);
@@ -107,18 +104,57 @@ class _FutureListener {
bool get hasErrorTest => (state == STATE_CATCHERROR_TEST);
bool get handlesComplete => (state == STATE_WHENCOMPLETE);
- _FutureOnValue get _onValue {
+
+ _FutureOnValue<S, T> get _onValue {
assert(handlesValue);
- return callback;
+ return callback as Object /*=_FutureOnValue<S, T>*/;
}
Function get _onError => errorCallback;
_FutureErrorTest get _errorTest {
assert(hasErrorTest);
- return callback;
+ return callback as Object /*=_FutureErrorTest*/;
}
_FutureAction get _whenCompleteAction {
assert(handlesComplete);
- return callback;
+ return callback as Object /*=_FutureAction*/;
+ }
+
+ /// 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;
+ }
+
+ dynamic/*T|Future<T>*/ handleValue(S sourceResult) {
+ return _zone.runUnary/*<dynamic/*T|Future<T>*/, S>*/(
+ _onValue, sourceResult);
+ }
+
+ bool matchesErrorTest(AsyncError asyncError) {
+ if (!hasErrorTest) return true;
+ _FutureErrorTest test = _errorTest;
+ return _zone.runUnary/*<bool, dynamic>*/(_errorTest, asyncError.error);
+ }
+
+ dynamic/*T|Future<T>*/ handleError(AsyncError asyncError) {
+ assert(handlesError && hasErrorCallback);
+ if (errorCallback is ZoneBinaryCallback) {
+ var typedErrorCallback = errorCallback as Object
+ /*=ZoneBinaryCallback<Object/*T|Future<T>*/, Object, StackTrace>*/;
+ return _zone.runBinary(typedErrorCallback,
+ asyncError.error,
+ asyncError.stackTrace);
+ } else {
+ return _zone.runUnary/*<dynamic/*T|Future<T>*/, dynamic>*/(
+ errorCallback, asyncError.error);
+ }
+ }
+
+ dynamic handleWhenComplete() {
+ assert(!handlesError);
+ return _zone.run(_whenCompleteAction);
}
}
@@ -133,8 +169,7 @@ class _Future<T> implements Future<T> {
static const int _PENDING_COMPLETE = 1;
/// The future has been chained to another future. The result of that
/// other future becomes the result of this future as well.
- // TODO(floitsch): we don't really need a special "_CHAINED" state. We could
- // just use the PENDING_COMPLETE state instead.
+ /// [resultOrListeners] contains the source future.
static const int _CHAINED = 2;
/// The future has been completed with a value result.
static const int _VALUE = 4;
@@ -167,14 +202,10 @@ class _Future<T> implements Future<T> {
* 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.
- *
- * The cases are disjoint - incomplete and unchained ([_INCOMPLETE]),
- * incomplete and chained ([_CHAINED]), or completed with value or error
- * ([_VALUE] or [_ERROR]) - so the field only needs to hold
- * one value at a time.
*/
var _resultOrListeners;
+ // This constructor is used by async/await.
_Future();
/// Valid types for value: `T` or `Future<T>`.
@@ -186,70 +217,77 @@ class _Future<T> implements Future<T> {
_asyncCompleteError(error, stackTrace);
}
- bool get _mayComplete => _state == _INCOMPLETE;
- bool get _isChained => _state == _CHAINED;
- bool get _isComplete => _state >= _VALUE;
- bool get _hasValue => _state == _VALUE;
- bool get _hasError => _state == _ERROR;
-
- set _isChained(bool value) {
- if (value) {
- assert(!_isComplete);
- _state = _CHAINED;
- } else {
- assert(_isChained);
- _state = _INCOMPLETE;
- }
+ bool get _mayComplete => _state == _INCOMPLETE;
+ bool get _isPendingComplete => _state == _PENDING_COMPLETE;
+ bool get _mayAddListener => _state <= _PENDING_COMPLETE;
+ bool get _isChained => _state == _CHAINED;
+ bool get _isComplete => _state >= _VALUE;
+ bool get _hasError => _state == _ERROR;
+
+ void _setChained(_Future source) {
+ assert(_mayAddListener);
+ _state = _CHAINED;
+ _resultOrListeners = source;
}
- Future/*<S>*/ then/*<S>*/(/*=S*/ f(T value), { Function onError }) {
- _Future/*<S>*/ result = new _Future();
- if (!identical(result._zone, _ROOT_ZONE)) {
- f = result._zone.registerUnaryCallback(f);
+ Future/*<E>*/ then/*<E>*/(
+ /*=dynamic/*E|Future<E>*/*/ f(T value), { Function onError }) {
+ Zone currentZone = Zone.current;
+ ZoneUnaryCallback registered;
+ if (!identical(currentZone, _ROOT_ZONE)) {
+ f = currentZone.registerUnaryCallback/*<dynamic, T>*/(f);
if (onError != null) {
- onError = _registerErrorHandler(onError, result._zone);
+ onError = _registerErrorHandler/*<T>*/(onError, currentZone);
}
}
- _addListener(new _FutureListener.then(result, f, onError));
+ return _thenNoZoneRegistration/*<E>*/(f, onError);
+ }
+
+ // This method is used by async/await.
+ Future/*<E>*/ _thenNoZoneRegistration/*<E>*/(f(T value), Function onError) {
+ _Future/*<E>*/ result = new _Future/*<E>*/();
+ _addListener(new _FutureListener/*<T, E>*/.then(result, f, onError));
return result;
}
- Future catchError(Function onError, { bool test(error) }) {
- _Future result = new _Future();
+ Future/*<T>*/ catchError(Function onError, { bool test(error) }) {
+ _Future/*<T>*/ result = new _Future/*<T>*/();
if (!identical(result._zone, _ROOT_ZONE)) {
- onError = _registerErrorHandler(onError, result._zone);
+ onError = _registerErrorHandler/*<T>*/(onError, result._zone);
if (test != null) test = result._zone.registerUnaryCallback(test);
}
- _addListener(new _FutureListener.catchError(result, onError, test));
+ _addListener(new _FutureListener/*<T, T>*/.catchError(
+ result, onError, test));
return result;
}
Future<T> whenComplete(action()) {
- _Future result = new _Future<T>();
+ _Future<T> result = new _Future<T>();
if (!identical(result._zone, _ROOT_ZONE)) {
- action = result._zone.registerCallback(action);
+ action = result._zone.registerCallback/*<dynamic>*/(action);
}
- _addListener(new _FutureListener.whenComplete(result, action));
+ _addListener(new _FutureListener/*<T, T>*/.whenComplete(result, action));
return result;
}
- Stream<T> asStream() => new Stream.fromFuture(this);
+ Stream<T> asStream() => new Stream<T>.fromFuture(this);
- void _markPendingCompletion() {
- if (!_mayComplete) throw new StateError("Future already completed");
+ void _setPendingComplete() {
+ assert(_mayComplete);
_state = _PENDING_COMPLETE;
}
- T get _value {
- assert(_isComplete && _hasValue);
+ AsyncError get _error {
+ assert(_hasError);
return _resultOrListeners;
}
- AsyncError get _error {
- assert(_isComplete && _hasError);
+ _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 = _VALUE;
@@ -266,16 +304,70 @@ class _Future<T> implements Future<T> {
_setErrorObject(new AsyncError(error, stackTrace));
}
+ /// 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;
+ _resultOrListeners = source._resultOrListeners;
+ }
+
void _addListener(_FutureListener listener) {
assert(listener._nextListener == null);
- if (_isComplete) {
+ 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;
+ while (cursor._nextListener != null) {
+ cursor = cursor._nextListener;
+ }
+ cursor._nextListener = existingListeners;
+ }
} else {
- listener._nextListener = _resultOrListeners;
- _resultOrListeners = listener;
+ 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);
+ });
}
}
@@ -285,7 +377,12 @@ class _Future<T> implements Future<T> {
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;
@@ -296,41 +393,54 @@ class _Future<T> implements Future<T> {
}
// Take the value (when completed) of source and complete target with that
- // value (or error). This function can chain all Futures, but is slower
- // for _Future than _chainCoreFuture - Use _chainCoreFuture in that case.
+ // value (or error). This function could chain all Futures, but is slower
+ // for _Future than _chainCoreFuture, so you must use _chainCoreFuture
+ // in that case.
static void _chainForeignFuture(Future source, _Future target) {
assert(!target._isComplete);
assert(source is! _Future);
// Mark the target as chained (and as such half-completed).
- target._isChained = true;
- source.then((value) {
- assert(target._isChained);
- target._completeWithValue(value);
- },
- // TODO(floitsch): eventually we would like to make this non-optional
- // and dependent on the listeners of the target future. If none of
- // the target future's listeners want to have the stack trace we don't
- // need a trace.
- onError: (error, [stackTrace]) {
- assert(target._isChained);
- target._completeError(error, stackTrace);
+ target._setPendingComplete();
+ try {
+ source.then((value) {
+ assert(target._isPendingComplete);
+ target._completeWithValue(value);
+ },
+ // TODO(floitsch): eventually we would like to make this non-optional
+ // and dependent on the listeners of the target future. If none of
+ // the target future's listeners want to have the stack trace we don't
+ // need a trace.
+ onError: (error, [stackTrace]) {
+ assert(target._isPendingComplete);
+ target._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(() {
+ target._completeError(e, s);
});
+ }
}
// Take the value (when completed) of source and complete target with that
// value (or error). This function expects that source is a _Future.
static void _chainCoreFuture(_Future source, _Future target) {
- assert(!target._isComplete);
- assert(source is _Future);
-
- // Mark the target as chained (and as such half-completed).
- target._isChained = true;
- _FutureListener listener = new _FutureListener.chain(target);
+ assert(target._mayAddListener); // Not completed, not already chained.
+ while (source._isChained) {
+ source = source._chainSource;
+ }
if (source._isComplete) {
- _propagateToListeners(source, listener);
+ _FutureListener listeners = target._removeListeners();
+ target._cloneResult(source);
+ _propagateToListeners(target, listeners);
} else {
- source._addListener(listener);
+ _FutureListener listeners = target._resultOrListeners;
+ target._setChained(source);
+ source._prependListeners(listeners);
}
}
@@ -344,12 +454,12 @@ class _Future<T> implements Future<T> {
}
} else {
_FutureListener listeners = _removeListeners();
- _setValue(value);
+ _setValue(value as Object /*=T*/);
_propagateToListeners(this, listeners);
}
}
- void _completeWithValue(value) {
+ void _completeWithValue(T value) {
assert(!_isComplete);
assert(value is! Future);
@@ -379,17 +489,15 @@ class _Future<T> implements Future<T> {
// unhandled error, even though we know we are already going to listen to
// it.
- if (value == null) {
- // No checks for `null`.
- } else if (value is Future) {
+ if (value is Future) {
// Assign to typed variables so we get earlier checks in checked mode.
- Future<T> typedFuture = value;
+ Future<T> typedFuture = value as Object /*=Future<T>*/;
if (typedFuture is _Future) {
_Future<T> coreFuture = typedFuture;
- if (coreFuture._isComplete && coreFuture._hasError) {
+ if (coreFuture._hasError) {
// Case 1 from above. Delay completion to enable the user to register
// callbacks.
- _markPendingCompletion();
+ _setPendingComplete();
_zone.scheduleMicrotask(() {
_chainCoreFuture(coreFuture, this);
});
@@ -397,26 +505,25 @@ class _Future<T> implements Future<T> {
_chainCoreFuture(coreFuture, this);
}
} else {
- // Case 2 from above. Chain the future immidiately.
+ // Case 2 from above. Chain the future immediately.
// Note that we are still completing asynchronously (through
- // _chainForeignFuture)..
+ // _chainForeignFuture).
_chainForeignFuture(typedFuture, this);
}
return;
- } else {
- T typedValue = value;
}
+ T typedValue = value as Object /*=T*/;
- _markPendingCompletion();
+ _setPendingComplete();
_zone.scheduleMicrotask(() {
- _completeWithValue(value);
+ _completeWithValue(typedValue);
});
}
void _asyncCompleteError(error, StackTrace stackTrace) {
assert(!_isComplete);
- _markPendingCompletion();
+ _setPendingComplete();
_zone.scheduleMicrotask(() {
_completeError(error, stackTrace);
});
@@ -448,22 +555,21 @@ class _Future<T> implements Future<T> {
_propagateToListeners(source, listener);
}
_FutureListener listener = listeners;
+ final sourceResult = source._resultOrListeners;
// Do the actual propagation.
- // Set initial state of listenerHasValue and listenerValueOrError. These
- // variables are updated, with the outcome of potential callbacks.
- bool listenerHasValue = true;
- final sourceValue = hasError ? null : source._value;
- var listenerValueOrError = sourceValue;
- // Set to true if a whenComplete needs to wait for a future.
- // The whenComplete action will resume the propagation by itself.
- bool isPropagationAborted = false;
- // TODO(floitsch): mark the listener as pending completion. Currently
- // we can't do this, since the markPendingCompletion verifies that
- // the future is not already marked (or chained).
+ // 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
- // doesn't have callbacks, so this is a significant optimization.
- if (hasError || (listener.handlesValue || listener.handlesComplete)) {
+ // 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.
@@ -479,119 +585,99 @@ class _Future<T> implements Future<T> {
oldZone = Zone._enter(zone);
}
- bool handleValueCallback() {
+ // 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 {
- listenerValueOrError = zone.runUnary(listener._onValue,
- sourceValue);
- return true;
+ completeResult = listener.handleWhenComplete();
} catch (e, s) {
- listenerValueOrError = new AsyncError(e, s);
- return false;
- }
- }
-
- void handleError() {
- AsyncError asyncError = source._error;
- bool matchesTest = true;
- if (listener.hasErrorTest) {
- _FutureErrorTest test = listener._errorTest;
- try {
- matchesTest = zone.runUnary(test, asyncError.error);
- } catch (e, s) {
- listenerValueOrError = identical(asyncError.error, e) ?
- asyncError : new AsyncError(e, s);
- listenerHasValue = false;
- return;
+ if (hasError && identical(source._error.error, e)) {
+ listenerValueOrError = source._error;
+ } else {
+ listenerValueOrError = new AsyncError(e, s);
}
+ listenerHasError = true;
+ return;
}
- Function errorCallback = listener._onError;
- if (matchesTest && errorCallback != null) {
- try {
- if (errorCallback is ZoneBinaryCallback) {
- listenerValueOrError = zone.runBinary(errorCallback,
- asyncError.error,
- asyncError.stackTrace);
- } else {
- listenerValueOrError = zone.runUnary(errorCallback,
- asyncError.error);
+ if (completeResult is Future) {
+ if (completeResult is _Future && completeResult._isComplete) {
+ if (completeResult._hasError) {
+ listenerValueOrError = completeResult._error;
+ listenerHasError = true;
}
- } catch (e, s) {
- listenerValueOrError = identical(asyncError.error, e) ?
- asyncError : new AsyncError(e, s);
- listenerHasValue = false;
+ // Otherwise use the existing result of source.
return;
}
- listenerHasValue = true;
- } else {
- // Copy over the error from the source.
- listenerValueOrError = asyncError;
- listenerHasValue = false;
+ // 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;
+ listenerValueOrError = completeResult.then((_) => originalSource);
+ listenerHasError = false;
}
}
- void handleWhenCompleteCallback() {
- var completeResult;
+ void handleValueCallback() {
try {
- completeResult = zone.run(listener._whenCompleteAction);
+ listenerValueOrError = listener.handleValue(sourceResult);
} catch (e, s) {
- if (hasError && identical(source._error.error, e)) {
+ listenerValueOrError = new 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 = new AsyncError(e, s);
}
- listenerHasValue = false;
- return;
- }
- if (completeResult is Future) {
- _Future result = listener.result;
- result._isChained = true;
- isPropagationAborted = true;
- completeResult.then((ignored) {
- _propagateToListeners(source, new _FutureListener.chain(result));
- }, onError: (error, [stackTrace]) {
- // When there is an error, we have to make the error the new
- // result of the current listener.
- if (completeResult is! _Future) {
- // This should be a rare case.
- completeResult = new _Future();
- completeResult._setError(error, stackTrace);
- }
- _propagateToListeners(completeResult,
- new _FutureListener.chain(result));
- });
+ listenerHasError = true;
}
}
- if (!hasError) {
+
+ if (listener.handlesComplete) {
+ handleWhenCompleteCallback();
+ } else if (!hasError) {
if (listener.handlesValue) {
- listenerHasValue = handleValueCallback();
+ handleValueCallback();
}
} else {
- handleError();
- }
- if (listener.handlesComplete) {
- handleWhenCompleteCallback();
+ if (listener.handlesError) {
+ handleError();
+ }
}
+
// If we changed zone, oldZone will not be null.
if (oldZone != null) Zone._leave(oldZone);
- if (isPropagationAborted) return;
// If the listener's value is a future we need to chain it. Note that
- // this can only happen if there is a callback. Since 'is' checks
- // can be expensive, we're trying to avoid it.
- if (listenerHasValue &&
- !identical(sourceValue, listenerValueOrError) &&
- listenerValueOrError is Future) {
+ // this can only happen if there is a callback.
+ if (listenerValueOrError is Future) {
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) {
- // propagate the value (simulating a tail call).
- result._isChained = true;
+ listeners = result._removeListeners();
+ result._cloneResult(chainSource);
source = chainSource;
- listeners = new _FutureListener.chain(result);
continue;
} else {
_chainCoreFuture(chainSource, result);
@@ -604,7 +690,7 @@ class _Future<T> implements Future<T> {
}
_Future result = listener.result;
listeners = result._removeListeners();
- if (listenerHasValue) {
+ if (!listenerHasError) {
result._setValue(listenerValueOrError);
} else {
AsyncError asyncError = listenerValueOrError;
@@ -615,9 +701,9 @@ class _Future<T> implements Future<T> {
}
}
- Future timeout(Duration timeLimit, {onTimeout()}) {
+ Future<T> timeout(Duration timeLimit, {onTimeout()}) {
if (_isComplete) return new _Future.immediate(this);
- _Future result = new _Future();
+ _Future<T> result = new _Future<T>();
Timer timer;
if (onTimeout == null) {
timer = new Timer(timeLimit, () {
« no previous file with comments | « tool/input_sdk/lib/async/future.dart ('k') | tool/input_sdk/lib/async/schedule_microtask.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698