| 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 */ | 7 /** The onValue and onError handlers return either a value or a future */ |
| 8 typedef dynamic _FutureOnValue<T>(T value); | 8 typedef dynamic _FutureOnValue<T>(T value); |
| 9 /** Test used by [Future.catchError] to handle skip some errors. */ | 9 /** Test used by [Future.catchError] to handle skip some errors. */ |
| 10 typedef bool _FutureErrorTest(var error); | 10 typedef bool _FutureErrorTest(var error); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 88 _FutureListener.catchError(this.result, | 88 _FutureListener.catchError(this.result, |
| 89 this.errorCallback, _FutureErrorTest test) | 89 this.errorCallback, _FutureErrorTest test) |
| 90 : callback = test, | 90 : callback = test, |
| 91 state = (test == null) ? STATE_CATCHERROR : STATE_CATCHERROR_TEST; | 91 state = (test == null) ? STATE_CATCHERROR : STATE_CATCHERROR_TEST; |
| 92 | 92 |
| 93 _FutureListener.whenComplete(this.result, _FutureAction onComplete) | 93 _FutureListener.whenComplete(this.result, _FutureAction onComplete) |
| 94 : callback = onComplete, | 94 : callback = onComplete, |
| 95 errorCallback = null, | 95 errorCallback = null, |
| 96 state = STATE_WHENCOMPLETE; | 96 state = STATE_WHENCOMPLETE; |
| 97 | 97 |
| 98 _FutureListener.chain(this.result) | |
| 99 : callback = null, | |
| 100 errorCallback = null, | |
| 101 state = STATE_CHAIN; | |
| 102 | |
| 103 Zone get _zone => result._zone; | 98 Zone get _zone => result._zone; |
| 104 | 99 |
| 105 bool get handlesValue => (state & MASK_VALUE != 0); | 100 bool get handlesValue => (state & MASK_VALUE != 0); |
| 106 bool get handlesError => (state & MASK_ERROR != 0); | 101 bool get handlesError => (state & MASK_ERROR != 0); |
| 107 bool get hasErrorTest => (state == STATE_CATCHERROR_TEST); | 102 bool get hasErrorTest => (state == STATE_CATCHERROR_TEST); |
| 108 bool get handlesComplete => (state == STATE_WHENCOMPLETE); | 103 bool get handlesComplete => (state == STATE_WHENCOMPLETE); |
| 109 | 104 |
| 110 _FutureOnValue get _onValue { | 105 _FutureOnValue get _onValue { |
| 111 assert(handlesValue); | 106 assert(handlesValue); |
| 112 return callback; | 107 return callback; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 126 /// Initial state, waiting for a result. In this state, the | 121 /// Initial state, waiting for a result. In this state, the |
| 127 /// [resultOrListeners] field holds a single-linked list of | 122 /// [resultOrListeners] field holds a single-linked list of |
| 128 /// [_FutureListener] listeners. | 123 /// [_FutureListener] listeners. |
| 129 static const int _INCOMPLETE = 0; | 124 static const int _INCOMPLETE = 0; |
| 130 /// Pending completion. Set when completed using [_asyncComplete] or | 125 /// Pending completion. Set when completed using [_asyncComplete] or |
| 131 /// [_asyncCompleteError]. It is an error to try to complete it again. | 126 /// [_asyncCompleteError]. It is an error to try to complete it again. |
| 132 /// [resultOrListeners] holds listeners. | 127 /// [resultOrListeners] holds listeners. |
| 133 static const int _PENDING_COMPLETE = 1; | 128 static const int _PENDING_COMPLETE = 1; |
| 134 /// The future has been chained to another future. The result of that | 129 /// The future has been chained to another future. The result of that |
| 135 /// other future becomes the result of this future as well. | 130 /// other future becomes the result of this future as well. |
| 136 // TODO(floitsch): we don't really need a special "_CHAINED" state. We could | 131 /// [resultOrListeners] contains the source future. |
| 137 // just use the PENDING_COMPLETE state instead. | |
| 138 static const int _CHAINED = 2; | 132 static const int _CHAINED = 2; |
| 139 /// The future has been completed with a value result. | 133 /// The future has been completed with a value result. |
| 140 static const int _VALUE = 4; | 134 static const int _VALUE = 4; |
| 141 /// The future has been completed with an error result. | 135 /// The future has been completed with an error result. |
| 142 static const int _ERROR = 8; | 136 static const int _ERROR = 8; |
| 143 | 137 |
| 144 /** Whether the future is complete, and as what. */ | 138 /** Whether the future is complete, and as what. */ |
| 145 int _state = _INCOMPLETE; | 139 int _state = _INCOMPLETE; |
| 146 | 140 |
| 147 /** | 141 /** |
| (...skipping 12 matching lines...) Expand all Loading... |
| 160 * A result is only stored when the future has completed. | 154 * A result is only stored when the future has completed. |
| 161 * | 155 * |
| 162 * The listeners is an internally linked list of [_FutureListener]s. | 156 * The listeners is an internally linked list of [_FutureListener]s. |
| 163 * Listeners are only remembered while the future is not yet complete, | 157 * Listeners are only remembered while the future is not yet complete, |
| 164 * and it is not chained to another future. | 158 * and it is not chained to another future. |
| 165 * | 159 * |
| 166 * The future is another future that his future is chained to. This future | 160 * The future is another future that his future is chained to. This future |
| 167 * is waiting for the other future to complete, and when it does, this future | 161 * is waiting for the other future to complete, and when it does, this future |
| 168 * will complete with the same result. | 162 * will complete with the same result. |
| 169 * All listeners are forwarded to the other future. | 163 * All listeners are forwarded to the other future. |
| 170 * | |
| 171 * The cases are disjoint - incomplete and unchained ([_INCOMPLETE]), | |
| 172 * incomplete and chained ([_CHAINED]), or completed with value or error | |
| 173 * ([_VALUE] or [_ERROR]) - so the field only needs to hold | |
| 174 * one value at a time. | |
| 175 */ | 164 */ |
| 176 var _resultOrListeners; | 165 var _resultOrListeners; |
| 177 | 166 |
| 178 // This constructor is used by async/await. | 167 // This constructor is used by async/await. |
| 179 _Future(); | 168 _Future(); |
| 180 | 169 |
| 181 /// Valid types for value: `T` or `Future<T>`. | 170 /// Valid types for value: `T` or `Future<T>`. |
| 182 _Future.immediate(value) { | 171 _Future.immediate(value) { |
| 183 _asyncComplete(value); | 172 _asyncComplete(value); |
| 184 } | 173 } |
| 185 | 174 |
| 186 _Future.immediateError(var error, [StackTrace stackTrace]) { | 175 _Future.immediateError(var error, [StackTrace stackTrace]) { |
| 187 _asyncCompleteError(error, stackTrace); | 176 _asyncCompleteError(error, stackTrace); |
| 188 } | 177 } |
| 189 | 178 |
| 190 bool get _mayComplete => _state == _INCOMPLETE; | 179 bool get _mayComplete => _state == _INCOMPLETE; |
| 191 bool get _isPendingComplete => _state == _PENDING_COMPLETE; | 180 bool get _isPendingComplete => _state == _PENDING_COMPLETE; |
| 181 bool get _mayAddListener => _state <= _PENDING_COMPLETE; |
| 192 bool get _isChained => _state == _CHAINED; | 182 bool get _isChained => _state == _CHAINED; |
| 193 bool get _isComplete => _state >= _VALUE; | 183 bool get _isComplete => _state >= _VALUE; |
| 194 bool get _hasValue => _state == _VALUE; | |
| 195 bool get _hasError => _state == _ERROR; | 184 bool get _hasError => _state == _ERROR; |
| 196 | 185 |
| 197 void _setChained() { | 186 void _setChained(_Future source) { |
| 198 assert(!_isComplete); | 187 assert(_mayAddListener); |
| 199 _state = _CHAINED; | 188 _state = _CHAINED; |
| 189 _resultOrListeners = source; |
| 200 } | 190 } |
| 201 | 191 |
| 202 Future then(f(T value), { Function onError }) { | 192 Future then(f(T value), { Function onError }) { |
| 203 Zone currentZone = Zone.current; | 193 Zone currentZone = Zone.current; |
| 204 if (!identical(currentZone, _ROOT_ZONE)) { | 194 if (!identical(currentZone, _ROOT_ZONE)) { |
| 205 f = currentZone.registerUnaryCallback(f); | 195 f = currentZone.registerUnaryCallback(f); |
| 206 if (onError != null) { | 196 if (onError != null) { |
| 207 onError = _registerErrorHandler(onError, currentZone); | 197 onError = _registerErrorHandler(onError, currentZone); |
| 208 } | 198 } |
| 209 } | 199 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 231 _Future result = new _Future<T>(); | 221 _Future result = new _Future<T>(); |
| 232 if (!identical(result._zone, _ROOT_ZONE)) { | 222 if (!identical(result._zone, _ROOT_ZONE)) { |
| 233 action = result._zone.registerCallback(action); | 223 action = result._zone.registerCallback(action); |
| 234 } | 224 } |
| 235 _addListener(new _FutureListener.whenComplete(result, action)); | 225 _addListener(new _FutureListener.whenComplete(result, action)); |
| 236 return result; | 226 return result; |
| 237 } | 227 } |
| 238 | 228 |
| 239 Stream<T> asStream() => new Stream<T>.fromFuture(this); | 229 Stream<T> asStream() => new Stream<T>.fromFuture(this); |
| 240 | 230 |
| 241 void _markPendingCompletion() { | 231 void _setPendingComplete() { |
| 242 if (!_mayComplete) throw new StateError("Future already completed"); | 232 assert(_mayComplete); |
| 243 _state = _PENDING_COMPLETE; | 233 _state = _PENDING_COMPLETE; |
| 244 } | 234 } |
| 245 | 235 |
| 246 T get _value { | 236 AsyncError get _error { |
| 247 assert(_isComplete && _hasValue); | 237 assert(_hasError); |
| 248 return _resultOrListeners; | 238 return _resultOrListeners; |
| 249 } | 239 } |
| 250 | 240 |
| 251 AsyncError get _error { | 241 _Future get _chainSource { |
| 252 assert(_isComplete && _hasError); | 242 assert(_isChained); |
| 253 return _resultOrListeners; | 243 return _resultOrListeners; |
| 254 } | 244 } |
| 255 | 245 |
| 256 // This method is used by async/await. | 246 // This method is used by async/await. |
| 257 void _setValue(T value) { | 247 void _setValue(T value) { |
| 258 assert(!_isComplete); // But may have a completion pending. | 248 assert(!_isComplete); // But may have a completion pending. |
| 259 _state = _VALUE; | 249 _state = _VALUE; |
| 260 _resultOrListeners = value; | 250 _resultOrListeners = value; |
| 261 } | 251 } |
| 262 | 252 |
| 263 void _setErrorObject(AsyncError error) { | 253 void _setErrorObject(AsyncError error) { |
| 264 assert(!_isComplete); // But may have a completion pending. | 254 assert(!_isComplete); // But may have a completion pending. |
| 265 _state = _ERROR; | 255 _state = _ERROR; |
| 266 _resultOrListeners = error; | 256 _resultOrListeners = error; |
| 267 } | 257 } |
| 268 | 258 |
| 269 void _setError(Object error, StackTrace stackTrace) { | 259 void _setError(Object error, StackTrace stackTrace) { |
| 270 _setErrorObject(new AsyncError(error, stackTrace)); | 260 _setErrorObject(new AsyncError(error, stackTrace)); |
| 271 } | 261 } |
| 272 | 262 |
| 263 /// Copy the completion result of [source] into this future. |
| 264 /// |
| 265 /// Used when a chained future notices that its source is completed. |
| 266 void _cloneResult(_Future source) { |
| 267 assert(!_isComplete); |
| 268 assert(source._isComplete); |
| 269 _state = source._state; |
| 270 _resultOrListeners = source._resultOrListeners; |
| 271 } |
| 272 |
| 273 void _addListener(_FutureListener listener) { | 273 void _addListener(_FutureListener listener) { |
| 274 assert(listener._nextListener == null); | 274 assert(listener._nextListener == null); |
| 275 if (_isComplete) { | 275 if (_mayAddListener) { |
| 276 listener._nextListener = _resultOrListeners; |
| 277 _resultOrListeners = listener; |
| 278 } else { |
| 279 if (_isChained) { |
| 280 // Delegate listeners to chained source future. |
| 281 // If the source is complete, instead copy its values and |
| 282 // drop the chaining. |
| 283 _Future source = _chainSource; |
| 284 if (!source._isComplete) { |
| 285 source._addListener(listener); |
| 286 return; |
| 287 } |
| 288 _cloneResult(source); |
| 289 } |
| 290 assert(_isComplete); |
| 276 // Handle late listeners asynchronously. | 291 // Handle late listeners asynchronously. |
| 277 _zone.scheduleMicrotask(() { | 292 _zone.scheduleMicrotask(() { |
| 278 _propagateToListeners(this, listener); | 293 _propagateToListeners(this, listener); |
| 279 }); | 294 }); |
| 280 } else { | |
| 281 listener._nextListener = _resultOrListeners; | |
| 282 _resultOrListeners = listener; | |
| 283 } | 295 } |
| 284 } | 296 } |
| 285 | 297 |
| 298 void _prependListeners(_FutureListener listeners) { |
| 299 if (listeners == null) return; |
| 300 if (_mayAddListener) { |
| 301 _FutureListener existingListeners = _resultOrListeners; |
| 302 _resultOrListeners = listeners; |
| 303 if (existingListeners != null) { |
| 304 _FutureListener cursor = listeners; |
| 305 while (cursor._nextListener != null) { |
| 306 cursor = cursor._nextListener; |
| 307 } |
| 308 cursor._nextListener = existingListeners; |
| 309 } |
| 310 } else { |
| 311 if (_isChained) { |
| 312 // Delegate listeners to chained source future. |
| 313 // If the source is complete, instead copy its values and |
| 314 // drop the chaining. |
| 315 _Future source = _chainSource; |
| 316 if (!source._isComplete) { |
| 317 source._prependListeners(listeners); |
| 318 return; |
| 319 } |
| 320 _cloneResult(source); |
| 321 } |
| 322 assert(_isComplete); |
| 323 listeners = _reverseListeners(listeners); |
| 324 _zone.scheduleMicrotask(() { |
| 325 _propagateToListeners(this, listeners); |
| 326 }); |
| 327 } |
| 328 } |
| 329 |
| 286 _FutureListener _removeListeners() { | 330 _FutureListener _removeListeners() { |
| 287 // Reverse listeners before returning them, so the resulting list is in | 331 // Reverse listeners before returning them, so the resulting list is in |
| 288 // subscription order. | 332 // subscription order. |
| 289 assert(!_isComplete); | 333 assert(!_isComplete); |
| 290 _FutureListener current = _resultOrListeners; | 334 _FutureListener current = _resultOrListeners; |
| 291 _resultOrListeners = null; | 335 _resultOrListeners = null; |
| 336 return _reverseListeners(current); |
| 337 } |
| 338 |
| 339 _FutureListener _reverseListeners(_FutureListener listeners) { |
| 292 _FutureListener prev = null; | 340 _FutureListener prev = null; |
| 341 _FutureListener current = listeners; |
| 293 while (current != null) { | 342 while (current != null) { |
| 294 _FutureListener next = current._nextListener; | 343 _FutureListener next = current._nextListener; |
| 295 current._nextListener = prev; | 344 current._nextListener = prev; |
| 296 prev = current; | 345 prev = current; |
| 297 current = next; | 346 current = next; |
| 298 } | 347 } |
| 299 return prev; | 348 return prev; |
| 300 } | 349 } |
| 301 | 350 |
| 302 // Take the value (when completed) of source and complete target with that | 351 // Take the value (when completed) of source and complete target with that |
| 303 // value (or error). This function could chain all Futures, but is slower | 352 // value (or error). This function could chain all Futures, but is slower |
| 304 // for _Future than _chainCoreFuture, so you must use _chainCoreFuture | 353 // for _Future than _chainCoreFuture, so you must use _chainCoreFuture |
| 305 // in that case. | 354 // in that case. |
| 306 static void _chainForeignFuture(Future source, _Future target) { | 355 static void _chainForeignFuture(Future source, _Future target) { |
| 307 assert(!target._isComplete); | 356 assert(!target._isComplete); |
| 308 assert(source is! _Future); | 357 assert(source is! _Future); |
| 309 | 358 |
| 310 // Mark the target as chained (and as such half-completed). | 359 // Mark the target as chained (and as such half-completed). |
| 311 target._setChained(); | 360 target._setPendingComplete(); |
| 312 try { | 361 try { |
| 313 source.then((value) { | 362 source.then((value) { |
| 314 assert(target._isChained); | 363 assert(target._isPendingComplete); |
| 315 target._completeWithValue(value); | 364 target._completeWithValue(value); |
| 316 }, | 365 }, |
| 317 // TODO(floitsch): eventually we would like to make this non-optional | 366 // TODO(floitsch): eventually we would like to make this non-optional |
| 318 // and dependent on the listeners of the target future. If none of | 367 // and dependent on the listeners of the target future. If none of |
| 319 // the target future's listeners want to have the stack trace we don't | 368 // the target future's listeners want to have the stack trace we don't |
| 320 // need a trace. | 369 // need a trace. |
| 321 onError: (error, [stackTrace]) { | 370 onError: (error, [stackTrace]) { |
| 322 assert(target._isChained); | 371 assert(target._isPendingComplete); |
| 323 target._completeError(error, stackTrace); | 372 target._completeError(error, stackTrace); |
| 324 }); | 373 }); |
| 325 } catch (e, s) { | 374 } catch (e, s) { |
| 326 // This only happens if the `then` call threw synchronously when given | 375 // This only happens if the `then` call threw synchronously when given |
| 327 // valid arguments. | 376 // valid arguments. |
| 328 // That requires a non-conforming implementation of the Future interface, | 377 // That requires a non-conforming implementation of the Future interface, |
| 329 // which should, hopefully, never happen. | 378 // which should, hopefully, never happen. |
| 330 scheduleMicrotask(() { | 379 scheduleMicrotask(() { |
| 331 target._completeError(e, s); | 380 target._completeError(e, s); |
| 332 }); | 381 }); |
| 333 } | 382 } |
| 334 } | 383 } |
| 335 | 384 |
| 336 // Take the value (when completed) of source and complete target with that | 385 // Take the value (when completed) of source and complete target with that |
| 337 // value (or error). This function expects that source is a _Future. | 386 // value (or error). This function expects that source is a _Future. |
| 338 static void _chainCoreFuture(_Future source, _Future target) { | 387 static void _chainCoreFuture(_Future source, _Future target) { |
| 339 assert(!target._isComplete); | 388 assert(target._mayAddListener); // Not completed, not already chained. |
| 340 assert(source is _Future); | 389 while (source._isChained) { |
| 341 | 390 source = source._chainSource; |
| 342 // Mark the target as chained (and as such half-completed). | 391 } |
| 343 target._setChained(); | |
| 344 _FutureListener listener = new _FutureListener.chain(target); | |
| 345 if (source._isComplete) { | 392 if (source._isComplete) { |
| 346 _propagateToListeners(source, listener); | 393 _FutureListener listeners = target._removeListeners(); |
| 394 target._cloneResult(source); |
| 395 _propagateToListeners(target, listeners); |
| 347 } else { | 396 } else { |
| 348 source._addListener(listener); | 397 _FutureListener listeners = target._resultOrListeners; |
| 398 target._setChained(source); |
| 399 source._prependListeners(listeners); |
| 349 } | 400 } |
| 350 } | 401 } |
| 351 | 402 |
| 352 void _complete(value) { | 403 void _complete(value) { |
| 353 assert(!_isComplete); | 404 assert(!_isComplete); |
| 354 if (value is Future) { | 405 if (value is Future) { |
| 355 if (value is _Future) { | 406 if (value is _Future) { |
| 356 _chainCoreFuture(value, this); | 407 _chainCoreFuture(value, this); |
| 357 } else { | 408 } else { |
| 358 _chainForeignFuture(value, this); | 409 _chainForeignFuture(value, this); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 if (value == null) { | 448 if (value == null) { |
| 398 // No checks for `null`. | 449 // No checks for `null`. |
| 399 } else if (value is Future) { | 450 } else if (value is Future) { |
| 400 // Assign to typed variables so we get earlier checks in checked mode. | 451 // Assign to typed variables so we get earlier checks in checked mode. |
| 401 Future<T> typedFuture = value; | 452 Future<T> typedFuture = value; |
| 402 if (typedFuture is _Future) { | 453 if (typedFuture is _Future) { |
| 403 _Future<T> coreFuture = typedFuture; | 454 _Future<T> coreFuture = typedFuture; |
| 404 if (coreFuture._hasError) { | 455 if (coreFuture._hasError) { |
| 405 // Case 1 from above. Delay completion to enable the user to register | 456 // Case 1 from above. Delay completion to enable the user to register |
| 406 // callbacks. | 457 // callbacks. |
| 407 _markPendingCompletion(); | 458 _setPendingComplete(); |
| 408 _zone.scheduleMicrotask(() { | 459 _zone.scheduleMicrotask(() { |
| 409 _chainCoreFuture(coreFuture, this); | 460 _chainCoreFuture(coreFuture, this); |
| 410 }); | 461 }); |
| 411 } else { | 462 } else { |
| 412 _chainCoreFuture(coreFuture, this); | 463 _chainCoreFuture(coreFuture, this); |
| 413 } | 464 } |
| 414 } else { | 465 } else { |
| 415 // Case 2 from above. Chain the future immidiately. | 466 // Case 2 from above. Chain the future immidiately. |
| 416 // Note that we are still completing asynchronously (through | 467 // Note that we are still completing asynchronously (through |
| 417 // _chainForeignFuture). | 468 // _chainForeignFuture). |
| 418 _chainForeignFuture(typedFuture, this); | 469 _chainForeignFuture(typedFuture, this); |
| 419 } | 470 } |
| 420 return; | 471 return; |
| 421 } else { | 472 } else { |
| 422 T typedValue = value; | 473 T typedValue = value; |
| 474 assert(typedValue is T); // Avoid warning that typedValue is unused. |
| 423 } | 475 } |
| 424 | 476 |
| 425 _markPendingCompletion(); | 477 _setPendingComplete(); |
| 426 _zone.scheduleMicrotask(() { | 478 _zone.scheduleMicrotask(() { |
| 427 _completeWithValue(value); | 479 _completeWithValue(value); |
| 428 }); | 480 }); |
| 429 } | 481 } |
| 430 | 482 |
| 431 void _asyncCompleteError(error, StackTrace stackTrace) { | 483 void _asyncCompleteError(error, StackTrace stackTrace) { |
| 432 assert(!_isComplete); | 484 assert(!_isComplete); |
| 433 | 485 |
| 434 _markPendingCompletion(); | 486 _setPendingComplete(); |
| 435 _zone.scheduleMicrotask(() { | 487 _zone.scheduleMicrotask(() { |
| 436 _completeError(error, stackTrace); | 488 _completeError(error, stackTrace); |
| 437 }); | 489 }); |
| 438 } | 490 } |
| 439 | 491 |
| 440 /** | 492 /** |
| 441 * Propagates the value/error of [source] to its [listeners], executing the | 493 * Propagates the value/error of [source] to its [listeners], executing the |
| 442 * listeners' callbacks. | 494 * listeners' callbacks. |
| 443 */ | 495 */ |
| 444 static void _propagateToListeners(_Future source, _FutureListener listeners) { | 496 static void _propagateToListeners(_Future source, _FutureListener listeners) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 456 // Usually futures only have one listener. If they have several, we | 508 // Usually futures only have one listener. If they have several, we |
| 457 // call handle them separately in recursive calls, continuing | 509 // call handle them separately in recursive calls, continuing |
| 458 // here only when there is only one listener left. | 510 // here only when there is only one listener left. |
| 459 while (listeners._nextListener != null) { | 511 while (listeners._nextListener != null) { |
| 460 _FutureListener listener = listeners; | 512 _FutureListener listener = listeners; |
| 461 listeners = listener._nextListener; | 513 listeners = listener._nextListener; |
| 462 listener._nextListener = null; | 514 listener._nextListener = null; |
| 463 _propagateToListeners(source, listener); | 515 _propagateToListeners(source, listener); |
| 464 } | 516 } |
| 465 _FutureListener listener = listeners; | 517 _FutureListener listener = listeners; |
| 518 final sourceResult = source._resultOrListeners; |
| 466 // Do the actual propagation. | 519 // Do the actual propagation. |
| 467 // Set initial state of listenerHasError and listenerValueOrError. These | 520 // Set initial state of listenerHasError and listenerValueOrError. These |
| 468 // variables are updated with the outcome of potential callbacks. | 521 // variables are updated with the outcome of potential callbacks. |
| 522 // Non-error results, including futures, are stored in |
| 523 // listenerValueOrError and listenerHasError is set to false. Errors |
| 524 // are stored in listenerValueOrError as an [AsyncError] and |
| 525 // listenerHasError is set to true. |
| 469 bool listenerHasError = hasError; | 526 bool listenerHasError = hasError; |
| 470 final sourceResult = source._resultOrListeners; | |
| 471 var listenerValueOrError = sourceResult; | 527 var listenerValueOrError = sourceResult; |
| 472 | 528 |
| 473 // Only if we either have an error or callbacks, go into this, somewhat | 529 // Only if we either have an error or callbacks, go into this, somewhat |
| 474 // expensive, branch. Here we'll enter/leave the zone. Many futures | 530 // expensive, branch. Here we'll enter/leave the zone. Many futures |
| 475 // don't have callbacks, so this is a significant optimization. | 531 // don't have callbacks, so this is a significant optimization. |
| 476 if (hasError || listener.handlesValue || listener.handlesComplete) { | 532 if (hasError || listener.handlesValue || listener.handlesComplete) { |
| 477 Zone zone = listener._zone; | 533 Zone zone = listener._zone; |
| 478 if (hasError && !source._zone.inSameErrorZone(zone)) { | 534 if (hasError && !source._zone.inSameErrorZone(zone)) { |
| 479 // Don't cross zone boundaries with errors. | 535 // Don't cross zone boundaries with errors. |
| 480 AsyncError asyncError = source._error; | 536 AsyncError asyncError = source._error; |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 591 | 647 |
| 592 // If the listener's value is a future we need to chain it. Note that | 648 // If the listener's value is a future we need to chain it. Note that |
| 593 // this can only happen if there is a callback. | 649 // this can only happen if there is a callback. |
| 594 if (listenerValueOrError is Future) { | 650 if (listenerValueOrError is Future) { |
| 595 Future chainSource = listenerValueOrError; | 651 Future chainSource = listenerValueOrError; |
| 596 // Shortcut if the chain-source is already completed. Just continue | 652 // Shortcut if the chain-source is already completed. Just continue |
| 597 // the loop. | 653 // the loop. |
| 598 _Future result = listener.result; | 654 _Future result = listener.result; |
| 599 if (chainSource is _Future) { | 655 if (chainSource is _Future) { |
| 600 if (chainSource._isComplete) { | 656 if (chainSource._isComplete) { |
| 601 // propagate the value (simulating a tail call). | 657 listeners = result._removeListeners(); |
| 602 result._setChained(); | 658 result._cloneResult(chainSource); |
| 603 source = chainSource; | 659 source = chainSource; |
| 604 listeners = new _FutureListener.chain(result); | |
| 605 continue; | 660 continue; |
| 606 } else { | 661 } else { |
| 607 _chainCoreFuture(chainSource, result); | 662 _chainCoreFuture(chainSource, result); |
| 608 } | 663 } |
| 609 } else { | 664 } else { |
| 610 _chainForeignFuture(chainSource, result); | 665 _chainForeignFuture(chainSource, result); |
| 611 } | 666 } |
| 612 return; | 667 return; |
| 613 } | 668 } |
| 614 } | 669 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 } | 707 } |
| 653 }, onError: (e, s) { | 708 }, onError: (e, s) { |
| 654 if (timer.isActive) { | 709 if (timer.isActive) { |
| 655 timer.cancel(); | 710 timer.cancel(); |
| 656 result._completeError(e, s); | 711 result._completeError(e, s); |
| 657 } | 712 } |
| 658 }); | 713 }); |
| 659 return result; | 714 return result; |
| 660 } | 715 } |
| 661 } | 716 } |
| OLD | NEW |