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

Side by Side Diff: sdk/lib/async/future_impl.dart

Issue 1516783003: Make chained futures point to their source instead of opposite. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Address comments. Created 5 years 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698