Chromium Code Reviews| Index: sdk/lib/async/future_impl.dart |
| diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart |
| index 35dfd1194f052403843fff9fef9fef1ebcaf706e..c823c73e9dc97a8f1a0d47332edd18dfb8fec00c 100644 |
| --- a/sdk/lib/async/future_impl.dart |
| +++ b/sdk/lib/async/future_impl.dart |
| @@ -187,20 +187,16 @@ 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 _isChained => _state == _CHAINED; |
| + bool get _isComplete => _state >= _VALUE; |
| + bool get _hasValue => _state == _VALUE; |
| + bool get _hasError => _state == _ERROR; |
| + |
| + void _setChained() { |
| + assert(!_isComplete); |
| + _state = _CHAINED; |
| } |
| Future then(f(T value), { Function onError }) { |
| @@ -304,14 +300,15 @@ 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; |
| + target._setChained(); |
| try { |
| source.then((value) { |
| assert(target._isChained); |
| @@ -343,7 +340,7 @@ class _Future<T> implements Future<T> { |
| assert(source is _Future); |
| // Mark the target as chained (and as such half-completed). |
| - target._isChained = true; |
| + target._setChained(); |
| _FutureListener listener = new _FutureListener.chain(target); |
| if (source._isComplete) { |
| _propagateToListeners(source, listener); |
| @@ -404,7 +401,7 @@ class _Future<T> implements Future<T> { |
| Future<T> typedFuture = value; |
| 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(); |
| @@ -467,21 +464,16 @@ class _Future<T> implements Future<T> { |
| } |
| _FutureListener listener = listeners; |
| // 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. |
| + bool listenerHasError = hasError; |
| + final sourceResult = source._resultOrListeners; |
| + 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. |
|
floitsch
2015/12/09 22:52:01
don't
|
| - if (hasError || (listener.handlesValue || listener.handlesComplete)) { |
| + if (hasError || listener.handlesValue || listener.handlesComplete) { |
| Zone zone = listener._zone; |
| if (hasError && !source._zone.inSameErrorZone(zone)) { |
| // Don't cross zone boundaries with errors. |
| @@ -498,17 +490,18 @@ class _Future<T> implements Future<T> { |
| } |
| bool handleValueCallback() { |
|
floitsch
2015/12/09 22:52:01
doesn't return anything anymore.
Lasse Reichstein Nielsen
2015/12/10 06:52:26
Done.
|
| + assert(!hasError); |
| try { |
| listenerValueOrError = zone.runUnary(listener._onValue, |
| - sourceValue); |
| - return true; |
| + sourceResult); |
| + listenerHasError = false; |
| } catch (e, s) { |
| listenerValueOrError = new AsyncError(e, s); |
| - return false; |
| + listenerHasError = true; |
| } |
| } |
| - void handleError() { |
| + bool handleError() { |
|
floitsch
2015/12/09 22:52:02
doesn't return anything anymore.
Lasse Reichstein Nielsen
2015/12/10 06:52:26
Done.
|
| AsyncError asyncError = source._error; |
| bool matchesTest = true; |
| if (listener.hasErrorTest) { |
| @@ -516,9 +509,10 @@ class _Future<T> implements Future<T> { |
| try { |
| matchesTest = zone.runUnary(test, asyncError.error); |
| } catch (e, s) { |
| - listenerValueOrError = identical(asyncError.error, e) ? |
| - asyncError : new AsyncError(e, s); |
| - listenerHasValue = false; |
| + listenerValueOrError = identical(asyncError.error, e) |
| + ? asyncError |
| + : new AsyncError(e, s); |
| + listenerHasError = true; |
| return; |
| } |
| } |
| @@ -533,21 +527,19 @@ class _Future<T> implements Future<T> { |
| listenerValueOrError = zone.runUnary(errorCallback, |
| asyncError.error); |
| } |
| + listenerHasError = false; |
| } catch (e, s) { |
| - listenerValueOrError = identical(asyncError.error, e) ? |
| - asyncError : new AsyncError(e, s); |
| - listenerHasValue = false; |
| + listenerValueOrError = identical(asyncError.error, e) |
| + ? asyncError |
| + : new AsyncError(e, s); |
| + listenerHasError = true; |
| return; |
|
floitsch
2015/12/09 22:52:02
no need for the return here.
Lasse Reichstein Nielsen
2015/12/10 06:52:27
Done.
|
| } |
| - listenerHasValue = true; |
| - } else { |
| - // Copy over the error from the source. |
| - listenerValueOrError = asyncError; |
| - listenerHasValue = false; |
| } |
| } |
| void handleWhenCompleteCallback() { |
| + assert(!listener.handlesError); |
| var completeResult; |
| try { |
| completeResult = zone.run(listener._whenCompleteAction); |
| @@ -557,48 +549,52 @@ class _Future<T> implements Future<T> { |
| } else { |
| listenerValueOrError = new AsyncError(e, s); |
| } |
| - listenerHasValue = false; |
| + listenerHasError = true; |
| 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); |
| + if (completeResult is _Future && completeResult._isComplete) { |
| + if (completeResult._hasError) { |
| + listenerValueOrError = completeResult._error; |
| + listenerHasError = true; |
| } |
| - _propagateToListeners(completeResult, |
| - new _FutureListener.chain(result)); |
| - }); |
| + // Otherwise use the existing result of source. |
| + return; |
| + } |
| + // We have to wait for the compelteResult future to complete |
|
floitsch
2015/12/09 22:52:02
completeResult
Lasse Reichstein Nielsen
2015/12/10 06:52:26
Good catch. Done.
floitsch
2015/12/10 07:24:41
Your bar is low for "good catches" ;)
|
| + // before knowing if it's an error or we should use the result |
| + // of source. |
| + var originalSource = source; |
| + listenerValueOrError = completeResult.then((_) => originalSource); |
| + listenerHasError = false; |
| } |
| } |
| - if (!hasError) { |
| + if (listener.handlesComplete) { |
| + // 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); |
| + 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. |
|
floitsch
2015/12/09 22:52:02
Not sure if is-checks really are that expensive.
i
Lasse Reichstein Nielsen
2015/12/10 06:52:26
Done.
|
| - if (listenerHasValue && |
| - !identical(sourceValue, listenerValueOrError) && |
| + if (!listenerHasError && |
| + !identical(sourceResult, listenerValueOrError) && |
| listenerValueOrError is Future) { |
| Future chainSource = listenerValueOrError; |
| // Shortcut if the chain-source is already completed. Just continue |
| @@ -607,7 +603,7 @@ class _Future<T> implements Future<T> { |
| if (chainSource is _Future) { |
| if (chainSource._isComplete) { |
| // propagate the value (simulating a tail call). |
| - result._isChained = true; |
| + result._setChained(); |
| source = chainSource; |
| listeners = new _FutureListener.chain(result); |
| continue; |
| @@ -622,7 +618,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; |