Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.async; | 5 part of dart.async; |
| 6 | 6 |
| 7 /** The onValue and onError handlers return either a value or a future */ | |
| 8 typedef dynamic _FutureOnValue<T>(T value); | |
| 9 typedef dynamic _FutureOnError(error); | |
| 10 /** Test used by [Future.catchError] to handle skip some errors. */ | |
| 11 typedef bool _FutureErrorTest(var error); | |
| 12 /** Used by [WhenFuture]. */ | |
| 13 typedef _FutureAction(); | |
| 14 | |
| 7 abstract class _Completer<T> implements Completer<T> { | 15 abstract class _Completer<T> implements Completer<T> { |
| 8 final Future<T> future; | 16 final _Future<T> future = new _Future<T>(); |
| 9 bool _isComplete = false; | |
| 10 | 17 |
| 11 _Completer() : future = new _FutureImpl<T>() { | 18 void complete([T value]); |
| 12 _FutureImpl futureImpl = future; | |
| 13 futureImpl._zone.expectCallback(); | |
| 14 } | |
| 15 | 19 |
| 16 void _setFutureValue(T value); | 20 void completeError(Object error, [Object stackTrace = null]); |
| 17 void _setFutureError(error); | 21 |
| 22 // The future's _isComplete doesn't take into account pending completions. | |
| 23 // We therefore use _mayComplete. | |
| 24 bool get isCompleted => !future._mayComplete; | |
| 25 } | |
| 26 | |
| 27 class _AsyncCompleter<T> extends _Completer<T> { | |
| 18 | 28 |
| 19 void complete([T value]) { | 29 void complete([T value]) { |
| 20 if (_isComplete) throw new StateError("Future already completed"); | 30 future._asyncComplete(value); |
| 21 _isComplete = true; | |
| 22 _FutureImpl futureImpl = future; | |
| 23 _setFutureValue(value); | |
| 24 } | 31 } |
| 25 | 32 |
| 26 void completeError(Object error, [Object stackTrace = null]) { | 33 void completeError(Object error, [Object stackTrace = null]) { |
| 27 if (_isComplete) throw new StateError("Future already completed"); | 34 future._asyncCompleteError(error, stackTrace); |
| 28 _isComplete = true; | |
| 29 if (stackTrace != null) { | |
| 30 // Force the stack trace onto the error, even if it already had one. | |
| 31 _attachStackTrace(error, stackTrace); | |
| 32 } | |
| 33 _FutureImpl futureImpl = future; | |
| 34 _setFutureError(error); | |
| 35 } | |
| 36 | |
| 37 bool get isCompleted => _isComplete; | |
| 38 } | |
| 39 | |
| 40 class _AsyncCompleter<T> extends _Completer<T> { | |
| 41 void _setFutureValue(T value) { | |
| 42 _FutureImpl future = this.future; | |
| 43 future._asyncSetValue(value); | |
| 44 // The async-error will schedule another callback, so we can cancel | |
| 45 // the expectation without shutting down the zone. | |
| 46 future._zone.cancelCallbackExpectation(); | |
| 47 } | |
| 48 | |
| 49 void _setFutureError(error) { | |
| 50 _FutureImpl future = this.future; | |
| 51 future._asyncSetError(error); | |
| 52 // The async-error will schedule another callback, so we can cancel | |
| 53 // the expectation without shutting down the zone. | |
| 54 future._zone.cancelCallbackExpectation(); | |
| 55 } | 35 } |
| 56 } | 36 } |
| 57 | 37 |
| 58 class _SyncCompleter<T> extends _Completer<T> { | 38 class _SyncCompleter<T> extends _Completer<T> { |
| 59 void _setFutureValue(T value) { | 39 |
| 60 _FutureImpl future = this.future; | 40 void complete([T value]) { |
| 61 future._setValue(value); | 41 future._complete(value); |
| 62 future._zone.cancelCallbackExpectation(); | |
| 63 } | 42 } |
| 64 | 43 |
| 65 void _setFutureError(error) { | 44 void completeError(Object error, [Object stackTrace = null]) { |
| 66 _FutureImpl future = this.future; | 45 future._completeError(error, stackTrace); |
| 67 future._setError(error); | |
| 68 future._zone.cancelCallbackExpectation(); | |
| 69 } | 46 } |
| 70 } | 47 } |
| 71 | 48 |
| 72 /** | 49 class _Future<T> implements Future<T> { |
| 73 * A listener on a future. | |
| 74 * | |
| 75 * When the future completes, the [_sendValue] or [_sendError] method | |
| 76 * is invoked with the result. | |
| 77 * | |
| 78 * Listeners are kept in a linked list. | |
| 79 */ | |
| 80 abstract class _FutureListener<T> { | |
| 81 _FutureListener _nextListener; | |
| 82 factory _FutureListener.wrap(_FutureImpl future) { | |
| 83 return new _FutureListenerWrapper(future); | |
| 84 } | |
| 85 void _sendValue(T value); | |
| 86 void _sendError(error); | |
| 87 | |
| 88 bool _inSameErrorZone(_Zone otherZone); | |
| 89 } | |
| 90 | |
| 91 /** Adapter for a [_FutureImpl] to be a future result listener. */ | |
| 92 class _FutureListenerWrapper<T> implements _FutureListener<T> { | |
| 93 _FutureImpl future; | |
| 94 _FutureListener _nextListener; | |
| 95 _FutureListenerWrapper(this.future); | |
| 96 _sendValue(T value) { future._setValueUnchecked(value); } | |
| 97 _sendError(error) { future._setErrorUnchecked(error); } | |
| 98 bool _inSameErrorZone(_Zone otherZone) => future._inSameErrorZone(otherZone); | |
| 99 } | |
| 100 | |
| 101 /** | |
| 102 * This listener is installed at error-zone boundaries. It signals an | |
| 103 * uncaught error in the zone of origin when an error is sent from one error | |
| 104 * zone to another. | |
| 105 * | |
| 106 * When a Future is listening to another Future and they have not been | |
| 107 * instantiated in the same error-zone then Futures put an instance of this | |
| 108 * class between them (see [_FutureImpl._addListener]). | |
| 109 * | |
| 110 * For example: | |
| 111 * | |
| 112 * var completer = new Completer(); | |
| 113 * var future = completer.future.then((x) => x); | |
| 114 * catchErrors(() { | |
| 115 * var future2 = future.catchError(print); | |
| 116 * }); | |
| 117 * completer.completeError(499); | |
| 118 * | |
| 119 * In this example `future` and `future2` are in different error-zones. The | |
| 120 * error (499) that originates outside `catchErrors` must not reach the | |
| 121 * `catchError` future (`future2`) inside `catchErrors`. | |
| 122 * | |
| 123 * When invoking `catchError` on `future` the Future installs an | |
| 124 * [_ErrorZoneBoundaryListener] between itself and the result, `future2`. | |
| 125 * | |
| 126 * Conceptually _ErrorZoneBoundaryListeners could be implemented as | |
| 127 * `catchError`s on the origin future as well. | |
| 128 */ | |
| 129 class _ErrorZoneBoundaryListener implements _FutureListener { | |
| 130 _FutureListener _nextListener; | |
| 131 final _FutureListener _listener; | |
| 132 | |
| 133 _ErrorZoneBoundaryListener(this._listener); | |
| 134 | |
| 135 bool _inSameErrorZone(_Zone otherZone) { | |
| 136 // Should never be called. We use [_inSameErrorZone] to know if we have | |
| 137 // to insert an instance of [_ErrorZoneBoundaryListener] (and in the | |
| 138 // controller). Once we have inserted one we should never need to use it | |
| 139 // anymore. | |
| 140 throw new UnsupportedError( | |
| 141 "A Zone boundary doesn't support the inSameErrorZone test."); | |
| 142 } | |
| 143 | |
| 144 void _sendValue(value) { | |
| 145 _listener._sendValue(value); | |
| 146 } | |
| 147 | |
| 148 void _sendError(error) { | |
| 149 // We are not allowed to send an error from one error-zone to another. | |
| 150 // This is the whole purpose of this class. | |
| 151 _Zone.current.handleUncaughtError(error); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 class _FutureImpl<T> implements Future<T> { | |
| 156 // State of the future. The state determines the interpretation of the | 50 // State of the future. The state determines the interpretation of the |
| 157 // [resultOrListeners] field. | 51 // [resultOrListeners] field. |
| 158 // TODO(lrn): rename field since it can also contain a chained future. | 52 // TODO(lrn): rename field since it can also contain a chained future. |
| 159 | 53 |
| 160 /// Initial state, waiting for a result. In this state, the | 54 /// Initial state, waiting for a result. In this state, the |
| 161 /// [resultOrListeners] field holds a single-linked list of | 55 /// [resultOrListeners] field holds a single-linked list of |
| 162 /// [FutureListener] listeners. | 56 /// [FutureListener] listeners. |
| 163 static const int _INCOMPLETE = 0; | 57 static const int _INCOMPLETE = 0; |
| 164 /// Pending completion. Set when completed using [_asyncSetValue] or | 58 /// Pending completion. Set when completed using [_asyncComplete] or |
| 165 /// [_asyncSetError]. It is an error to try to complete it again. | 59 /// [_asyncCompleteError]. It is an error to try to complete it again. |
| 166 static const int _PENDING_COMPLETE = 1; | 60 static const int _PENDING_COMPLETE = 1; |
| 167 /// The future has been chained to another future. The result of that | 61 /// The future has been chained to another future. The result of that |
| 168 /// other future becomes the result of this future as well. | 62 /// other future becomes the result of this future as well. |
| 169 /// In this state, the [resultOrListeners] field holds the future that | 63 /// In this state, no callback should be executed anymore. |
| 170 /// will give the result to this future. Both existing and new listeners are | 64 // TODO(floitsch): we don't really need a special "_CHAINED" state. We could |
| 171 /// forwarded directly to the other future. | 65 // just use the PENDING_COMPLETE state instead. |
| 172 static const int _CHAINED = 2; | 66 static const int _CHAINED = 2; |
| 173 /// The future has been chained to another future, but there hasn't been | |
| 174 /// any listeners added to this future yet. If it is completed with an | |
| 175 /// error, the error will be considered unhandled. | |
| 176 static const int _CHAINED_UNLISTENED = 6; | |
| 177 /// The future has been completed with a value result. | 67 /// The future has been completed with a value result. |
| 178 static const int _VALUE = 8; | 68 static const int _VALUE = 4; |
| 179 /// The future has been completed with an error result. | 69 /// The future has been completed with an error result. |
| 180 static const int _ERROR = 12; | 70 static const int _ERROR = 8; |
| 181 | 71 |
| 182 /** Whether the future is complete, and as what. */ | 72 /** Whether the future is complete, and as what. */ |
| 183 int _state = _INCOMPLETE; | 73 int _state = _INCOMPLETE; |
| 184 | 74 |
| 185 final _Zone _zone = _Zone.current.fork(); | 75 final _Zone _zone = _Zone.current.fork(); |
| 186 | 76 |
| 187 bool get _isChained => (_state & _CHAINED) != 0; | 77 bool get _mayComplete => _state == _INCOMPLETE; |
| 188 bool get _hasChainedListener => _state == _CHAINED; | 78 bool get _isChained => _state == _CHAINED; |
| 189 bool get _isComplete => _state >= _VALUE; | 79 bool get _isComplete => _state >= _VALUE; |
| 190 bool get _mayComplete => _state == _INCOMPLETE; | |
| 191 bool get _hasValue => _state == _VALUE; | 80 bool get _hasValue => _state == _VALUE; |
| 192 bool get _hasError => _state >= _ERROR; | 81 bool get _hasError => _state == _ERROR; |
| 82 | |
| 83 set _isChained(bool value) { | |
| 84 if (value) { | |
| 85 assert(_mayComplete); | |
| 86 _state = _CHAINED; | |
| 87 } else { | |
| 88 assert(_isChained); | |
| 89 _state = _INCOMPLETE; | |
| 90 } | |
| 91 } | |
| 193 | 92 |
| 194 /** | 93 /** |
| 195 * Either the result, a list of listeners or another future. | 94 * Either the result, a list of listeners or another future. |
| 196 * | 95 * |
| 197 * The result of the future is either a value or an error. | 96 * The result of the future is either a value or an error. |
| 198 * A result is only stored when the future has completed. | 97 * A result is only stored when the future has completed. |
| 199 * | 98 * |
| 200 * The listeners is an internally linked list of [_FutureListener]s. | 99 * The listeners is an internally linked list of [_FutureListener]s. |
| 201 * Listeners are only remembered while the future is not yet complete, | 100 * Listeners are only remembered while the future is not yet complete, |
| 202 * and it is not chained to another future. | 101 * and it is not chained to another future. |
| 203 * | 102 * |
| 204 * The future is another future that his future is chained to. This future | 103 * The future is another future that his future is chained to. This future |
| 205 * is waiting for the other future to complete, and when it does, this future | 104 * is waiting for the other future to complete, and when it does, this future |
| 206 * will complete with the same result. | 105 * will complete with the same result. |
| 207 * All listeners are forwarded to the other future. | 106 * All listeners are forwarded to the other future. |
| 208 * | 107 * |
| 209 * The cases are disjoint (incomplete and unchained, incomplete and | 108 * The cases are disjoint (incomplete and unchained, incomplete and |
| 210 * chained, or completed with value or error), so the field only needs to hold | 109 * chained, or completed with value or error), so the field only needs to hold |
| 211 * one value at a time. | 110 * one value at a time. |
| 212 */ | 111 */ |
| 213 var _resultOrListeners; | 112 var _resultOrListeners; |
| 214 | 113 |
| 215 _FutureImpl(); | 114 _Future _nextListener; |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
Extra field, no documentation? What is this?
Lasse Reichstein Nielsen
2013/09/10 13:52:12
If a single listener is the common case, how about
floitsch
2013/09/10 17:19:14
The same as before (line 81). Will add documentati
floitsch
2013/09/10 17:19:14
Completely agree. Adding a TODO.
| |
| 216 | 115 |
| 217 _FutureImpl.immediate(T value) { | 116 // TODO(floitsch): we only need two closure fields to store the callbacks. |
| 117 // If we store the type of a closure in the state field (where there are | |
| 118 // still bits left), we can just store two closures instead of using 4 | |
| 119 // fields of which 2 are always null. | |
| 120 final _FutureOnValue _onValueCallback; | |
| 121 final _FutureErrorTest _errorTestCallback; | |
| 122 final _FutureOnError _onErrorCallback; | |
| 123 final _FutureAction _whenCompleteActionCallback; | |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
Four more fields? Even two?
How about just one fie
Lasse Reichstein Nielsen
2013/09/10 13:52:12
I am concerned about having the extra overhead on
floitsch
2013/09/10 17:19:14
See TODO above.
Will optimize later.
floitsch
2013/09/10 17:19:14
Agreed. We will have lots of time to optimize...
| |
| 124 | |
| 125 _FutureOnValue get _onValue => _isChained ? null : _onValueCallback; | |
| 126 _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback; | |
| 127 _FutureOnError get _onError => _isChained ? null : _onErrorCallback; | |
| 128 _FutureAction get _whenCompleteAction | |
| 129 => _isChained ? null : _whenCompleteActionCallback; | |
| 130 | |
| 131 _Future() | |
| 132 : _onValueCallback = null, _errorTestCallback = null, | |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
Default is null, you don't have to write all these
Lasse Reichstein Nielsen
2013/09/10 13:52:12
My bad.
floitsch
2013/09/10 17:19:14
That is not correct. Final fields need to be initi
| |
| 133 _onErrorCallback = null, _whenCompleteActionCallback = null; | |
| 134 | |
| 135 _Future.immediate(T value) | |
| 136 : _onValueCallback = null, _errorTestCallback = null, | |
| 137 _onErrorCallback = null, _whenCompleteActionCallback = null { | |
| 138 _asyncComplete(value); | |
| 139 } | |
| 140 | |
| 141 _Future.immediateError(var error, [Object stackTrace]) | |
| 142 : _onValueCallback = null, _errorTestCallback = null, | |
| 143 _onErrorCallback = null, _whenCompleteActionCallback = null { | |
| 144 _asyncCompleteError(error, stackTrace); | |
| 145 } | |
| 146 | |
| 147 _Future._then(this._onValueCallback, this._onErrorCallback) | |
| 148 : _errorTestCallback = null, _whenCompleteActionCallback = null { | |
| 149 _zone.expectCallback(); | |
| 150 } | |
| 151 | |
| 152 _Future._catchError(this._onErrorCallback, this._errorTestCallback) | |
| 153 : _onValueCallback = null, _whenCompleteActionCallback = null { | |
| 154 _zone.expectCallback(); | |
| 155 } | |
| 156 | |
| 157 _Future._whenComplete(this._whenCompleteActionCallback) | |
| 158 : _onValueCallback = null, _errorTestCallback = null, | |
| 159 _onErrorCallback = null { | |
| 160 _zone.expectCallback(); | |
| 161 } | |
| 162 | |
| 163 Future then(f(T value), { onError(error) }) { | |
| 164 _Future result; | |
| 165 result = new _Future._then(f, onError); | |
| 166 _addListener(result); | |
| 167 return result; | |
| 168 } | |
| 169 | |
| 170 Future catchError(f(error), { bool test(error) }) { | |
| 171 _Future result = new _Future._catchError(f, test); | |
| 172 _addListener(result); | |
| 173 return result; | |
| 174 } | |
| 175 | |
| 176 Future<T> whenComplete(action()) { | |
| 177 _Future result = new _Future<T>._whenComplete(action); | |
| 178 _addListener(result); | |
| 179 return result; | |
| 180 } | |
| 181 | |
| 182 Stream<T> asStream() => new Stream.fromFuture(this); | |
| 183 | |
| 184 void _markPendingCompletion() { | |
| 185 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 186 _state = _PENDING_COMPLETE; | |
| 187 } | |
| 188 | |
| 189 void _clearPendingCompletion() { | |
| 190 assert(_state == _PENDING_COMPLETE); | |
| 191 _state = _INCOMPLETE; | |
| 192 } | |
| 193 | |
| 194 T get _value { | |
| 195 assert(_isComplete && _hasValue); | |
| 196 return _resultOrListeners; | |
| 197 } | |
| 198 | |
| 199 Object get _error { | |
| 200 assert(_isComplete && _hasError); | |
| 201 return _resultOrListeners; | |
| 202 } | |
| 203 | |
| 204 void _setValue(T value) { | |
| 205 assert(!_isComplete); // But may have a completion pending. | |
| 218 _state = _VALUE; | 206 _state = _VALUE; |
| 219 _resultOrListeners = value; | 207 _resultOrListeners = value; |
| 220 } | 208 } |
| 221 | 209 |
| 222 _FutureImpl.immediateError(var error, [Object stackTrace]) { | 210 void _setError(Object error) { |
| 223 if (stackTrace != null) { | 211 assert(!_isComplete); // But may have a completion pending. |
| 224 // Force stack trace onto error, even if it had already one. | 212 _state = _ERROR; |
| 225 _attachStackTrace(error, stackTrace); | 213 _resultOrListeners = error; |
| 226 } | |
| 227 _asyncSetError(error); | |
| 228 } | 214 } |
| 229 | 215 |
| 230 factory _FutureImpl.wait(Iterable<Future> futures) { | 216 void _addListener(_Future listener) { |
| 231 Completer completer; | |
| 232 // List collecting values from the futures. | |
| 233 // Set to null if an error occurs. | |
| 234 List values; | |
| 235 void handleError(error) { | |
| 236 if (values != null) { | |
| 237 values = null; | |
| 238 completer.completeError(error); | |
| 239 } | |
| 240 } | |
| 241 // As each future completes, put its value into the corresponding | |
| 242 // position in the list of values. | |
| 243 int remaining = 0; | |
| 244 for (Future future in futures) { | |
| 245 int pos = remaining++; | |
| 246 future.catchError(handleError).then((Object value) { | |
| 247 if (values == null) return null; | |
| 248 values[pos] = value; | |
| 249 remaining--; | |
| 250 if (remaining == 0) { | |
| 251 completer.complete(values); | |
| 252 } | |
| 253 }); | |
| 254 } | |
| 255 if (remaining == 0) { | |
| 256 return new Future.value(const []); | |
| 257 } | |
| 258 values = new List(remaining); | |
| 259 completer = new Completer<List>(); | |
| 260 return completer.future; | |
| 261 } | |
| 262 | |
| 263 Future then(f(T value), { onError(error) }) { | |
| 264 if (onError == null) { | |
| 265 return new _ThenFuture(f).._subscribeTo(this); | |
| 266 } | |
| 267 return new _SubscribeFuture(f, onError).._subscribeTo(this); | |
| 268 } | |
| 269 | |
| 270 Future catchError(f(error), { bool test(error) }) { | |
| 271 return new _CatchErrorFuture(f, test).._subscribeTo(this); | |
| 272 } | |
| 273 | |
| 274 Future<T> whenComplete(action()) { | |
| 275 return new _WhenFuture<T>(action).._subscribeTo(this); | |
| 276 } | |
| 277 | |
| 278 Stream<T> asStream() => new Stream.fromFuture(this); | |
| 279 | |
| 280 bool _inSameErrorZone(_Zone otherZone) { | |
| 281 return _zone.inSameErrorZone(otherZone); | |
| 282 } | |
| 283 | |
| 284 void _setValue(T value) { | |
| 285 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 286 _setValueUnchecked(value); | |
| 287 } | |
| 288 | |
| 289 void _setValueUnchecked(T value) { | |
| 290 _FutureListener listeners = _isChained ? null : _removeListeners(); | |
| 291 _state = _VALUE; | |
| 292 _resultOrListeners = value; | |
| 293 while (listeners != null) { | |
| 294 _FutureListener listener = listeners; | |
| 295 listeners = listener._nextListener; | |
| 296 listener._nextListener = null; | |
| 297 listener._sendValue(value); | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 void _setError(Object error) { | |
| 302 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 303 _setErrorUnchecked(error); | |
| 304 } | |
| 305 | |
| 306 void _setErrorUnchecked(Object error) { | |
| 307 _FutureListener listeners; | |
| 308 bool hasListeners; | |
| 309 if (_isChained) { | |
| 310 listeners = null; | |
| 311 hasListeners = (_state == _CHAINED); // and not _CHAINED_UNLISTENED. | |
| 312 } else { | |
| 313 listeners = _removeListeners(); | |
| 314 hasListeners = (listeners != null); | |
| 315 } | |
| 316 | |
| 317 _state = _ERROR; | |
| 318 _resultOrListeners = error; | |
| 319 | |
| 320 if (!hasListeners) { | |
| 321 // TODO(floitsch): Hook this into unhandled error handling. | |
| 322 var error = _resultOrListeners; | |
| 323 _zone.handleUncaughtError(error); | |
| 324 return; | |
| 325 } | |
| 326 while (listeners != null) { | |
| 327 _FutureListener listener = listeners; | |
| 328 listeners = listener._nextListener; | |
| 329 listener._nextListener = null; | |
| 330 listener._sendError(error); | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 void _asyncSetValue(T value) { | |
| 335 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 336 _state = _PENDING_COMPLETE; | |
| 337 runAsync(() { _setValueUnchecked(value); }); | |
| 338 } | |
| 339 | |
| 340 void _asyncSetError(Object error) { | |
| 341 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 342 _state = _PENDING_COMPLETE; | |
| 343 runAsync(() { _setErrorUnchecked(error); }); | |
| 344 } | |
| 345 | |
| 346 void _addListener(_FutureListener listener) { | |
| 347 assert(listener._nextListener == null); | 217 assert(listener._nextListener == null); |
| 348 if (!listener._inSameErrorZone(_zone)) { | |
| 349 listener = new _ErrorZoneBoundaryListener(listener); | |
| 350 } | |
| 351 if (_isChained) { | |
| 352 _state = _CHAINED; // In case it was _CHAINED_UNLISTENED. | |
| 353 _FutureImpl resultSource = _chainSource; | |
| 354 resultSource._addListener(listener); | |
| 355 return; | |
| 356 } | |
| 357 if (_isComplete) { | 218 if (_isComplete) { |
| 358 // Handle late listeners asynchronously. | 219 // Handle late listeners asynchronously. |
| 359 runAsync(() { | 220 runAsync(() { |
| 360 if (_hasValue) { | 221 _propagateToSuccessors(this, listener); |
| 361 T value = _resultOrListeners; | |
| 362 listener._sendValue(value); | |
| 363 } else { | |
| 364 assert(_hasError); | |
| 365 listener._sendError(_resultOrListeners); | |
| 366 } | |
| 367 }); | 222 }); |
| 368 } else { | 223 } else { |
| 369 assert(!_isComplete); | |
| 370 listener._nextListener = _resultOrListeners; | 224 listener._nextListener = _resultOrListeners; |
| 371 _resultOrListeners = listener; | 225 _resultOrListeners = listener; |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
Ah, now I get it (where "now" is reading some code
floitsch
2013/09/10 17:19:14
too clever :)
| |
| 372 } | 226 } |
| 373 } | 227 } |
| 374 | 228 |
| 375 _FutureListener _removeListeners() { | 229 _Future _removeListeners() { |
| 376 // Reverse listeners before returning them, so the resulting list is in | 230 // Reverse listeners before returning them, so the resulting list is in |
| 377 // subscription order. | 231 // subscription order. |
| 378 assert(!_isComplete); | 232 assert(!_isComplete); |
| 379 _FutureListener current = _resultOrListeners; | 233 _Future current = _resultOrListeners; |
| 380 _resultOrListeners = null; | 234 _resultOrListeners = null; |
| 381 _FutureListener prev = null; | 235 _Future prev = null; |
| 382 while (current != null) { | 236 while (current != null) { |
| 383 _FutureListener next = current._nextListener; | 237 _Future next = current._nextListener; |
| 384 current._nextListener = prev; | 238 current._nextListener = prev; |
| 385 prev = current; | 239 prev = current; |
| 386 current = next; | 240 current = next; |
| 387 } | 241 } |
| 388 return prev; | 242 return prev; |
| 389 } | 243 } |
| 390 | 244 |
| 245 static void _chainFutures(Future source, _Future target) { | |
| 246 assert(!target._isComplete); | |
| 247 | |
| 248 // Mark the target as chained (and as such half-completed). | |
| 249 target._isChained = true; | |
| 250 if (source is _Future) { | |
| 251 _Future internalFuture = source; | |
| 252 if (internalFuture._isComplete) { | |
| 253 _propagateToSuccessors(internalFuture, target); | |
| 254 } else { | |
| 255 internalFuture._addListener(target); | |
| 256 } | |
| 257 } else { | |
| 258 source.then((value) { | |
| 259 // Clear the is-chained bit, so that we can use the standard | |
| 260 // _complete method. | |
| 261 target._isChained = false; | |
| 262 target._complete(value); | |
| 263 }, | |
| 264 onError: (error) { | |
| 265 // Clear the is-chained bit, so that we can use the standard | |
| 266 // _completeError method. | |
| 267 target._isChained = false; | |
| 268 target._completeError(error); | |
| 269 }); | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 void _complete(value) { | |
| 274 assert(_onValueCallback == null && | |
| 275 _onErrorCallback == null && | |
| 276 _whenCompleteActionCallback == null && | |
| 277 _errorTestCallback == null); | |
| 278 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 279 if (value is Future) { | |
| 280 _chainFutures(value, this); | |
| 281 return; | |
| 282 } | |
| 283 _Future listeners = _removeListeners(); | |
| 284 _setValue(value); | |
| 285 _propagateToSuccessors(this, listeners); | |
| 286 } | |
| 287 | |
| 288 void _completeError(error, [StackTrace stackTrace]) { | |
| 289 assert(_onValueCallback == null && | |
| 290 _onErrorCallback == null && | |
| 291 _whenCompleteActionCallback == null && | |
| 292 _errorTestCallback == null); | |
| 293 // _isComplete does not trigger for pending completions. | |
| 294 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 295 if (stackTrace != null) { | |
| 296 // Force the stack trace onto the error, even if it already had one. | |
| 297 _attachStackTrace(error, stackTrace); | |
| 298 } | |
| 299 | |
| 300 _Future listeners = _isChained ? null : _removeListeners(); | |
| 301 _setError(error); | |
| 302 _propagateToSuccessors(this, listeners); | |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
Does it make sense to call this if listeners is nu
floitsch
2013/09/10 17:19:14
Yes. Because that's where we need to trigger an un
| |
| 303 } | |
| 304 | |
| 305 void _asyncComplete(value) { | |
| 306 assert(_onValueCallback == null && | |
| 307 _onErrorCallback == null && | |
| 308 _whenCompleteActionCallback == null && | |
| 309 _errorTestCallback == null); | |
| 310 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 311 // Two corner cases if the value is a future: | |
| 312 // 1. the future is already completed and an error. | |
| 313 // 2. the future is not yet completed but might become an error. | |
| 314 // The first case means that we must not immediately complete the Future, | |
| 315 // as our code would immediately start propagating the error without | |
| 316 // giving the time to install error-handlers. | |
| 317 // However the second case requires us to deal with the value immediately. | |
| 318 // Otherwise the value could complete with an error and report an | |
| 319 // unhandled error, even though we know we are already going to listen to | |
| 320 // it. | |
| 321 if (value is Future && | |
| 322 (value is! _Future || !(value as _Future)._isComplete)) { | |
| 323 // Case 2 from above. We need to register. | |
| 324 // Note that we are still completing asynchronously: either we register | |
| 325 // through .then (in which case the completing is asynchronous), or we | |
| 326 // have a _Future which isn't complete yet. | |
| 327 _complete(value); | |
| 328 return; | |
| 329 } | |
| 330 | |
| 331 _markPendingCompletion(); | |
| 332 runAsync(() { | |
| 333 _clearPendingCompletion(); | |
| 334 _complete(value); | |
| 335 }); | |
| 336 } | |
| 337 | |
| 338 _asyncCompleteError(error, [StackTrace stackTrace]) { | |
|
kevmoo-old
2013/09/09 19:27:45
void?
floitsch
2013/09/10 17:19:14
Done.
| |
| 339 assert(_onValueCallback == null && | |
| 340 _onErrorCallback == null && | |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
indent to after '('.
Or, preferably, make this fou
floitsch
2013/09/10 17:19:14
Done.
| |
| 341 _whenCompleteActionCallback == null && | |
| 342 _errorTestCallback == null); | |
| 343 if (!_mayComplete) throw new StateError("Future already completed"); | |
| 344 _markPendingCompletion(); | |
| 345 runAsync(() { | |
| 346 _clearPendingCompletion(); | |
| 347 _completeError(error, stackTrace); | |
| 348 }); | |
| 349 } | |
| 350 | |
| 391 /** | 351 /** |
| 392 * Make another [_FutureImpl] receive the result of this one. | 352 * Propagates the value/error of [source] to its [listeners], executing the |
| 393 * | 353 * listeners' callbacks. |
| 394 * If this future is already complete, the [future] is notified | 354 * |
| 395 * immediately. This function is only called during event resolution | 355 * If [runCallback] is true (which should be the default) it executes |
| 396 * where it's acceptable to send an event. | 356 * the registered action of listeners. If it is `false` then the callback is |
| 357 * skipped. This is used to complete futures with chained futures. | |
| 397 */ | 358 */ |
| 398 void _chain(_FutureImpl future) { | 359 static void _propagateToSuccessors(_Future source, _Future listeners) { |
| 399 if (!_isComplete) { | 360 while (true) { |
| 400 future._chainFromFuture(this); | 361 if (!source._isComplete) return; // Chained future. |
| 401 } else if (_hasValue) { | 362 bool hasError = source._hasError; |
| 402 future._setValue(_resultOrListeners); | 363 if (hasError && listeners == null) { |
| 403 } else { | 364 source._zone.handleUncaughtError(source._error); |
| 404 assert(_hasError); | 365 return; |
| 405 future._setError(_resultOrListeners); | 366 } |
| 406 } | 367 if (listeners == null) return; |
| 407 } | 368 _Future listener; |
| 408 | 369 _Future remainingListeners = listeners; |
| 409 /** | 370 do { |
| 410 * Returns the future that this future is chained to. | 371 // We handle each listener individually, using the stack as a queue. |
| 411 * | 372 // Usually there is only one listener, so this should not be a problem. |
| 412 * If that future is itself chained to something else, | 373 listener = remainingListeners; // Just an alias. |
| 413 * get the [_chainSource] of that future instead, and make this | 374 remainingListeners = remainingListeners._nextListener; |
| 414 * future chain directly to the earliest source. | 375 // Cut the connection to the remaining listeners. This way we can |
| 415 */ | 376 // call _propagateToSuccessors without fearing that it may have an |
| 416 _FutureImpl get _chainSource { | 377 // impact on other listeners. |
| 417 assert(_isChained); | 378 listener._nextListener = null; |
| 418 _FutureImpl future = _resultOrListeners; | 379 if (remainingListeners != null) { |
| 419 if (future._isChained) { | 380 _propagateToSuccessors(source, listener); |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
This seems like an awkward way to do a loop.
Drop
floitsch
2013/09/11 09:29:30
created helper function.
| |
| 420 future = _resultOrListeners = future._chainSource; | 381 } |
| 421 } | 382 } while (remainingListeners != null); |
| 422 return future; | 383 |
| 423 } | 384 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { |
| 424 | 385 // Don't cross zone boundaries with errors. |
| 425 /** | 386 source._zone.handleUncaughtError(source._error); |
| 426 * Make this incomplete future end up with the same result as [resultSource]. | 387 return; |
| 427 * | 388 } |
| 428 * This is done by moving all listeners to [resultSource] and forwarding all | 389 if (!identical(_Zone.current, listener._zone)) { |
| 429 * future [_addListener] calls to [resultSource] directly. | 390 // Run the propagation in the listener's zone to avoid |
| 430 */ | 391 // zone transitions. The idea is that many chained futures will |
| 431 void _chainFromFuture(_FutureImpl resultSource) { | 392 // be in the same zone. |
| 432 assert(!_isComplete); | 393 listener._zone.executePeriodicCallback(() { |
| 433 assert(!_isChained); | 394 _propagateToSuccessors(source, listeners); |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
listeners -> listener
(same thing, easier to read
floitsch
2013/09/10 17:19:14
Done.
| |
| 434 if (resultSource._isChained) { | 395 }); |
| 435 resultSource = resultSource._chainSource; | 396 return; |
| 436 } | 397 } |
| 437 assert(!resultSource._isChained); | 398 // Normal listener. |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
What does "normal" mean?
Seems to be "in the same
floitsch
2013/09/10 17:19:14
Replaced with "// Do actual propagation".
| |
| 438 if (identical(this, resultSource)) { | 399 // TODO(floitsch): Do we need to go through the zone even if we |
| 439 // The only unchained future in a future dependency tree (as defined | 400 // don't have a callback to execute? |
| 440 // by the chain-relations) is the "root" that every other future depends | 401 bool listenerHasValue; |
| 441 // on. The future we are adding is unchained, so if it is already in the | 402 var listenerValueOrError; |
| 442 // tree, it must be the root, so that's the only one we need to check | 403 // Set to true if a whenComplete needs to wait for a future. |
| 443 // against to detect a cycle. | 404 // The whenComplete action will resume the propagation by itself. |
| 444 _setError(new StateError("Cyclic future dependency.")); | 405 bool isPropagationAborted = false; |
| 445 return; | 406 // Even though we are already in the right zone (due to the optimization |
| 446 } | 407 // above), we still need to go through the zone. The overhead of |
| 447 _FutureListener cursor = _removeListeners(); | 408 // executeCallback is however smaller when it is already in the correct |
| 448 bool hadListeners = cursor != null; | 409 // zone. |
| 449 while (cursor != null) { | 410 // TODO(floitsch): only run callbacks in the zone, not the whole |
| 450 _FutureListener listener = cursor; | 411 // handling code. |
| 451 cursor = cursor._nextListener; | 412 listener._zone.executeCallback(() { |
| 452 listener._nextListener = null; | 413 // TODO(floitsch): mark the listener as pending completion. Currently |
| 453 resultSource._addListener(listener); | 414 // we can't do this, since the markPendingCompletion verifies that |
| 454 } | 415 // the future is not already marked (or chained). |
| 455 // Listen with this future as well, so that when the other future completes, | 416 try { |
| 456 // this future will be completed as well. | 417 if (!hasError) { |
| 457 resultSource._addListener(this._asListener()); | 418 var value = source._value; |
| 458 _resultOrListeners = resultSource; | 419 if (listener._onValue != null) { |
| 459 _state = hadListeners ? _CHAINED : _CHAINED_UNLISTENED; | 420 listenerValueOrError = listener._onValue(value); |
| 460 } | 421 listenerHasValue = true; |
| 461 | 422 } else { |
| 462 /** | 423 // Copy over the value from the source. |
| 463 * Helper function to handle the result of transforming an incoming event. | 424 listenerValueOrError = value; |
| 464 * | 425 listenerHasValue = true; |
| 465 * If the result is itself a [Future], this future is linked to that | 426 } |
| 466 * future's output. If not, this future is completed with the result. | 427 } else { |
| 467 */ | 428 Object error = source._error; |
| 468 void _setOrChainValue(var result) { | 429 _FutureErrorTest test = listener._errorTest; |
| 469 assert(!_isChained); | 430 bool matchesTest = true; |
| 470 assert(!_isComplete); | 431 if (test != null) { |
| 471 if (result is Future) { | 432 matchesTest = test(error); |
| 472 // Result should be a Future<T>. | 433 } |
| 473 if (result is _FutureImpl) { | 434 if (matchesTest && listener._onError != null) { |
| 474 _FutureImpl chainFuture = result; | 435 listenerValueOrError = listener._onError(error); |
| 475 chainFuture._chain(this); | 436 listenerHasValue = true; |
| 476 return; | 437 } else { |
| 438 // Copy over the error from the source. | |
| 439 listenerValueOrError = error; | |
| 440 listenerHasValue = false; | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 if (listener._whenCompleteAction != null) { | |
| 445 var completeResult = listener._whenCompleteAction(); | |
| 446 if (completeResult is Future) { | |
| 447 listener._isChained = true; | |
| 448 completeResult.then((ignored) { | |
| 449 // Try again, but this time don't run the whenComplete callback. | |
| 450 _propagateToSuccessors(source, listener); | |
| 451 }, onError: (error) { | |
| 452 // When there is an error, we have to make the error the new | |
| 453 // result of the current listener. | |
| 454 if (completeResult is! _Future) { | |
| 455 // This should be a rare case. | |
| 456 completeResult = new _Future(); | |
|
Lasse Reichstein Nielsen
2013/09/10 12:03:53
Is there an .immediateError constructor?
floitsch
2013/09/10 17:19:14
There is, but even though it is called "immediateE
| |
| 457 completeResult._setError(error); | |
| 458 } | |
| 459 _propagateToSuccessors(completeResult, listener); | |
| 460 }); | |
| 461 isPropagationAborted = true; | |
| 462 // We will reenter the listener's zone. | |
| 463 listener._zone.expectCallback(); | |
| 464 } | |
| 465 } | |
| 466 } catch (e, s) { | |
| 467 // Set the exception as error. | |
| 468 listenerValueOrError = _asyncError(e, s); | |
| 469 listenerHasValue = false; | |
| 470 } | |
| 471 if (listenerHasValue && listenerValueOrError is Future) { | |
| 472 // We are going to reenter the zone to finish what we started. | |
| 473 listener._zone.expectCallback(); | |
| 474 } | |
| 475 }); | |
| 476 if (isPropagationAborted) return; | |
| 477 // If the listener's value is a future we need to chain it. | |
| 478 if (listenerHasValue && listenerValueOrError is Future) { | |
| 479 Future chainSource = listenerValueOrError; | |
| 480 // Shortcut if the chain-source is already completed. Just continue the | |
| 481 // loop. | |
| 482 if (chainSource is _Future && (chainSource as _Future)._isComplete) { | |
| 483 // propagate the value (simulating a tail call). | |
| 484 listener._isChained = true; | |
| 485 source = chainSource; | |
| 486 listeners = listener; | |
| 487 continue; | |
| 488 } | |
| 489 _chainFutures(chainSource, listener); | |
| 490 return; | |
| 491 } | |
| 492 | |
| 493 if (listenerHasValue) { | |
| 494 listeners = listener._removeListeners(); | |
| 495 listener._setValue(listenerValueOrError); | |
| 477 } else { | 496 } else { |
| 478 Future future = result; | 497 listeners = listener._removeListeners(); |
| 479 future.then(_setValue, | 498 listener._setError(listenerValueOrError); |
| 480 onError: _setError); | 499 } |
| 481 return; | 500 // Prepare for next round. |
| 482 } | 501 source = listener; |
| 483 } else { | 502 } |
| 484 // Result must be of type T. | 503 } |
| 485 _setValue(result); | |
| 486 } | |
| 487 } | |
| 488 | |
| 489 _FutureListener _asListener() => new _FutureListener.wrap(this); | |
| 490 } | 504 } |
| 491 | |
| 492 /** | |
| 493 * Transforming future base class. | |
| 494 * | |
| 495 * A transforming future is itself a future and a future listener. | |
| 496 * Subclasses override [_sendValue]/[_sendError] to intercept | |
| 497 * the results of a previous future. | |
| 498 */ | |
| 499 abstract class _TransformFuture<S, T> extends _FutureImpl<T> | |
| 500 implements _FutureListener<S> { | |
| 501 // _FutureListener implementation. | |
| 502 _FutureListener _nextListener; | |
| 503 | |
| 504 _TransformFuture() { | |
| 505 _zone.expectCallback(); | |
| 506 } | |
| 507 | |
| 508 void _sendValue(S value) { | |
| 509 _zone.executeCallback(() => _zonedSendValue(value)); | |
| 510 } | |
| 511 | |
| 512 void _sendError(error) { | |
| 513 _zone.executeCallback(() => _zonedSendError(error)); | |
| 514 } | |
| 515 | |
| 516 void _subscribeTo(_FutureImpl future) { | |
| 517 future._addListener(this); | |
| 518 } | |
| 519 | |
| 520 void _zonedSendValue(S value); | |
| 521 void _zonedSendError(error); | |
| 522 } | |
| 523 | |
| 524 /** The onValue and onError handlers return either a value or a future */ | |
| 525 typedef dynamic _FutureOnValue<T>(T value); | |
| 526 typedef dynamic _FutureOnError(error); | |
| 527 /** Test used by [Future.catchError] to handle skip some errors. */ | |
| 528 typedef bool _FutureErrorTest(var error); | |
| 529 /** Used by [WhenFuture]. */ | |
| 530 typedef _FutureAction(); | |
| 531 | |
| 532 /** Future returned by [Future.then] with no [:onError:] parameter. */ | |
| 533 class _ThenFuture<S, T> extends _TransformFuture<S, T> { | |
| 534 // TODO(ahe): Restore type when feature is implemented in dart2js | |
| 535 // checked mode. | |
| 536 final /* _FutureOnValue<S> */ _onValue; | |
| 537 | |
| 538 _ThenFuture(this._onValue); | |
| 539 | |
| 540 _zonedSendValue(S value) { | |
| 541 assert(_onValue != null); | |
| 542 var result; | |
| 543 try { | |
| 544 result = _onValue(value); | |
| 545 } catch (e, s) { | |
| 546 _setError(_asyncError(e, s)); | |
| 547 return; | |
| 548 } | |
| 549 _setOrChainValue(result); | |
| 550 } | |
| 551 | |
| 552 void _zonedSendError(error) { | |
| 553 _setError(error); | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 /** Future returned by [Future.catchError]. */ | |
| 558 class _CatchErrorFuture<T> extends _TransformFuture<T,T> { | |
| 559 final _FutureErrorTest _test; | |
| 560 final _FutureOnError _onError; | |
| 561 | |
| 562 _CatchErrorFuture(this._onError, this._test); | |
| 563 | |
| 564 _zonedSendValue(T value) { | |
| 565 _setValue(value); | |
| 566 } | |
| 567 | |
| 568 _zonedSendError(error) { | |
| 569 assert(_onError != null); | |
| 570 // if _test is supplied, check if it returns true, otherwise just | |
| 571 // forward the error unmodified. | |
| 572 if (_test != null) { | |
| 573 bool matchesTest; | |
| 574 try { | |
| 575 matchesTest = _test(error); | |
| 576 } catch (e, s) { | |
| 577 _setError(_asyncError(e, s)); | |
| 578 return; | |
| 579 } | |
| 580 if (!matchesTest) { | |
| 581 _setError(error); | |
| 582 return; | |
| 583 } | |
| 584 } | |
| 585 // Act on the error, and use the result as this future's result. | |
| 586 var result; | |
| 587 try { | |
| 588 result = _onError(error); | |
| 589 } catch (e, s) { | |
| 590 _setError(_asyncError(e, s)); | |
| 591 return; | |
| 592 } | |
| 593 _setOrChainValue(result); | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 /** Future returned by [Future.then] with an [:onError:] parameter. */ | |
| 598 class _SubscribeFuture<S, T> extends _ThenFuture<S, T> { | |
| 599 final _FutureOnError _onError; | |
| 600 | |
| 601 _SubscribeFuture(onValue(S value), this._onError) : super(onValue); | |
| 602 | |
| 603 // The _sendValue method is inherited from ThenFuture. | |
| 604 | |
| 605 void _zonedSendError(error) { | |
| 606 assert(_onError != null); | |
| 607 var result; | |
| 608 try { | |
| 609 result = _onError(error); | |
| 610 } catch (e, s) { | |
| 611 _setError(_asyncError(e, s)); | |
| 612 return; | |
| 613 } | |
| 614 _setOrChainValue(result); | |
| 615 } | |
| 616 } | |
| 617 | |
| 618 /** Future returned by [Future.whenComplete]. */ | |
| 619 class _WhenFuture<T> extends _TransformFuture<T, T> { | |
| 620 final _FutureAction _action; | |
| 621 | |
| 622 _WhenFuture(this._action); | |
| 623 | |
| 624 void _zonedSendValue(T value) { | |
| 625 try { | |
| 626 var result = _action(); | |
| 627 if (result is Future) { | |
| 628 Future resultFuture = result; | |
| 629 resultFuture.then((_) { | |
| 630 _setValue(value); | |
| 631 }, onError: _setError); | |
| 632 return; | |
| 633 } | |
| 634 } catch (e, s) { | |
| 635 _setError(_asyncError(e, s)); | |
| 636 return; | |
| 637 } | |
| 638 _setValue(value); | |
| 639 } | |
| 640 | |
| 641 void _zonedSendError(error) { | |
| 642 try { | |
| 643 var result = _action(); | |
| 644 if (result is Future) { | |
| 645 Future resultFuture = result; | |
| 646 // TODO(lrn): Find a way to combine [error] into [e]. | |
| 647 resultFuture.then((_) { | |
| 648 _setError(error); | |
| 649 }, onError: _setError); | |
| 650 return; | |
| 651 } | |
| 652 } catch (e, s) { | |
| 653 error = _asyncError(e, s); | |
| 654 } | |
| 655 _setError(error); | |
| 656 } | |
| 657 } | |
| OLD | NEW |