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

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

Issue 25027004: Add second argument to Future error handlers. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Upload Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
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 typedef dynamic _FutureOnError(error);
10 /** Test used by [Future.catchError] to handle skip some errors. */ 9 /** Test used by [Future.catchError] to handle skip some errors. */
11 typedef bool _FutureErrorTest(var error); 10 typedef bool _FutureErrorTest(var error);
12 /** Used by [WhenFuture]. */ 11 /** Used by [WhenFuture]. */
13 typedef _FutureAction(); 12 typedef _FutureAction();
14 13
15 abstract class _Completer<T> implements Completer<T> { 14 abstract class _Completer<T> implements Completer<T> {
16 final _Future<T> future = new _Future<T>(); 15 final _Future<T> future = new _Future<T>();
17 16
18 void complete([T value]); 17 void complete([T value]);
19 18
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 // TODO(floitsch): since single listeners are the common case we should 122 // TODO(floitsch): since single listeners are the common case we should
124 // use a bit to indicate that the _resultOrListeners contains a container. 123 // use a bit to indicate that the _resultOrListeners contains a container.
125 _Future _nextListener; 124 _Future _nextListener;
126 125
127 // TODO(floitsch): we only need two closure fields to store the callbacks. 126 // TODO(floitsch): we only need two closure fields to store the callbacks.
128 // If we store the type of a closure in the state field (where there are 127 // If we store the type of a closure in the state field (where there are
129 // still bits left), we can just store two closures instead of using 4 128 // still bits left), we can just store two closures instead of using 4
130 // fields of which 2 are always null. 129 // fields of which 2 are always null.
131 final _FutureOnValue _onValueCallback; 130 final _FutureOnValue _onValueCallback;
132 final _FutureErrorTest _errorTestCallback; 131 final _FutureErrorTest _errorTestCallback;
133 final _FutureOnError _onErrorCallback; 132 final Function _onErrorCallback;
134 final _FutureAction _whenCompleteActionCallback; 133 final _FutureAction _whenCompleteActionCallback;
135 134
136 _FutureOnValue get _onValue => _isChained ? null : _onValueCallback; 135 _FutureOnValue get _onValue => _isChained ? null : _onValueCallback;
137 _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback; 136 _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback;
138 _FutureOnError get _onError => _isChained ? null : _onErrorCallback; 137 Function get _onError => _isChained ? null : _onErrorCallback;
139 _FutureAction get _whenCompleteAction 138 _FutureAction get _whenCompleteAction
140 => _isChained ? null : _whenCompleteActionCallback; 139 => _isChained ? null : _whenCompleteActionCallback;
141 140
142 _Future() 141 _Future()
143 : _zone = Zone.current, 142 : _zone = Zone.current,
144 _onValueCallback = null, _errorTestCallback = null, 143 _onValueCallback = null, _errorTestCallback = null,
145 _onErrorCallback = null, _whenCompleteActionCallback = null; 144 _onErrorCallback = null, _whenCompleteActionCallback = null;
146 145
147 /// Valid types for value: `T` or `Future<T>`. 146 /// Valid types for value: `T` or `Future<T>`.
148 _Future.immediate(value) 147 _Future.immediate(value)
149 : _zone = Zone.current, 148 : _zone = Zone.current,
150 _onValueCallback = null, _errorTestCallback = null, 149 _onValueCallback = null, _errorTestCallback = null,
151 _onErrorCallback = null, _whenCompleteActionCallback = null { 150 _onErrorCallback = null, _whenCompleteActionCallback = null {
152 _asyncComplete(value); 151 _asyncComplete(value);
153 } 152 }
154 153
155 _Future.immediateError(var error, [Object stackTrace]) 154 _Future.immediateError(var error, [Object stackTrace])
156 : _zone = Zone.current, 155 : _zone = Zone.current,
157 _onValueCallback = null, _errorTestCallback = null, 156 _onValueCallback = null, _errorTestCallback = null,
158 _onErrorCallback = null, _whenCompleteActionCallback = null { 157 _onErrorCallback = null, _whenCompleteActionCallback = null {
159 _asyncCompleteError(error, stackTrace); 158 _asyncCompleteError(error, stackTrace);
160 } 159 }
161 160
162 _Future._then(onValueCallback(value), onErrorCallback(e)) 161 _Future._then(onValueCallback(value), Function onErrorCallback)
163 : _zone = Zone.current, 162 : _zone = Zone.current,
164 _onValueCallback = Zone.current.registerUnaryCallback(onValueCallback), 163 _onValueCallback = Zone.current.registerUnaryCallback(onValueCallback),
165 _onErrorCallback = Zone.current.registerUnaryCallback(onErrorCallback), 164 _onErrorCallback = _registerErrorCallback(onErrorCallback),
166 _errorTestCallback = null, 165 _errorTestCallback = null,
167 _whenCompleteActionCallback = null; 166 _whenCompleteActionCallback = null;
168 167
169 _Future._catchError(onErrorCallback(e), bool errorTestCallback(e)) 168 _Future._catchError(Function onErrorCallback, bool errorTestCallback(e))
170 : _zone = Zone.current, 169 : _zone = Zone.current,
171 _onErrorCallback = Zone.current.registerUnaryCallback(onErrorCallback), 170 _onErrorCallback = _registerErrorCallback(onErrorCallback),
172 _errorTestCallback = Zone.current.registerUnaryCallback(errorTestCallback) , 171 _errorTestCallback =
172 Zone.current.registerUnaryCallback(errorTestCallback),
173 _onValueCallback = null, 173 _onValueCallback = null,
174 _whenCompleteActionCallback = null; 174 _whenCompleteActionCallback = null;
175 175
176 _Future._whenComplete(whenCompleteActionCallback()) 176 _Future._whenComplete(whenCompleteActionCallback())
177 : _zone = Zone.current, 177 : _zone = Zone.current,
178 _whenCompleteActionCallback = 178 _whenCompleteActionCallback =
179 Zone.current.registerCallback(whenCompleteActionCallback), 179 Zone.current.registerCallback(whenCompleteActionCallback),
180 _onValueCallback = null, 180 _onValueCallback = null,
181 _errorTestCallback = null, 181 _errorTestCallback = null,
182 _onErrorCallback = null; 182 _onErrorCallback = null;
183 183
184 Future then(f(T value), { onError(error) }) { 184 /// Register the given [errorCallback] in the current zone.
185 static Function _registerErrorCallback(Function errorCallback) {
186 if (errorCallback is ZoneBinaryCallback) {
187 return Zone.current.registerBinaryCallback(errorCallback);
188 } else {
189 return Zone.current.registerUnaryCallback(errorCallback);
190 }
191 }
192
193 Future then(f(T value), { Function onError }) {
185 _Future result; 194 _Future result;
186 result = new _Future._then(f, onError); 195 result = new _Future._then(f, onError);
187 _addListener(result); 196 _addListener(result);
188 return result; 197 return result;
189 } 198 }
190 199
191 Future catchError(f(error), { bool test(error) }) { 200 Future catchError(Function onError, { bool test(error) }) {
192 _Future result = new _Future._catchError(f, test); 201 _Future result = new _Future._catchError(onError, test);
193 _addListener(result); 202 _addListener(result);
194 return result; 203 return result;
195 } 204 }
196 205
197 Future<T> whenComplete(action()) { 206 Future<T> whenComplete(action()) {
198 _Future result = new _Future<T>._whenComplete(action); 207 _Future result = new _Future<T>._whenComplete(action);
199 _addListener(result); 208 _addListener(result);
200 return result; 209 return result;
201 } 210 }
202 211
203 Stream<T> asStream() => new Stream.fromFuture(this); 212 Stream<T> asStream() => new Stream.fromFuture(this);
204 213
205 void _markPendingCompletion() { 214 void _markPendingCompletion() {
206 if (!_mayComplete) throw new StateError("Future already completed"); 215 if (!_mayComplete) throw new StateError("Future already completed");
207 _state = _PENDING_COMPLETE; 216 _state = _PENDING_COMPLETE;
208 } 217 }
209 218
210 T get _value { 219 T get _value {
211 assert(_isComplete && _hasValue); 220 assert(_isComplete && _hasValue);
212 return _resultOrListeners; 221 return _resultOrListeners;
213 } 222 }
214 223
215 Object get _error { 224 _AsyncError get _error {
216 assert(_isComplete && _hasError); 225 assert(_isComplete && _hasError);
217 return _resultOrListeners; 226 return _resultOrListeners;
218 } 227 }
219 228
220 void _setValue(T value) { 229 void _setValue(T value) {
221 assert(!_isComplete); // But may have a completion pending. 230 assert(!_isComplete); // But may have a completion pending.
222 _state = _VALUE; 231 _state = _VALUE;
223 _resultOrListeners = value; 232 _resultOrListeners = value;
224 } 233 }
225 234
226 void _setError(Object error) { 235 void _setError(Object error, StackTrace stackTrace) {
227 assert(!_isComplete); // But may have a completion pending. 236 assert(!_isComplete); // But may have a completion pending.
228 _state = _ERROR; 237 _state = _ERROR;
229 _resultOrListeners = error; 238 _resultOrListeners = new _AsyncError(error, stackTrace);
230 } 239 }
231 240
232 void _addListener(_Future listener) { 241 void _addListener(_Future listener) {
233 assert(listener._nextListener == null); 242 assert(listener._nextListener == null);
234 if (_isComplete) { 243 if (_isComplete) {
235 // Handle late listeners asynchronously. 244 // Handle late listeners asynchronously.
236 _zone.scheduleMicrotask(() { 245 _zone.scheduleMicrotask(() {
237 _propagateToListeners(this, listener); 246 _propagateToListeners(this, listener);
238 }); 247 });
239 } else { 248 } else {
(...skipping 28 matching lines...) Expand all
268 if (internalFuture._isComplete) { 277 if (internalFuture._isComplete) {
269 _propagateToListeners(internalFuture, target); 278 _propagateToListeners(internalFuture, target);
270 } else { 279 } else {
271 internalFuture._addListener(target); 280 internalFuture._addListener(target);
272 } 281 }
273 } else { 282 } else {
274 source.then((value) { 283 source.then((value) {
275 assert(target._isChained); 284 assert(target._isChained);
276 target._complete(value); 285 target._complete(value);
277 }, 286 },
278 onError: (error) { 287 // TODO(floitsch): eventually we would like to make this non-optional
288 // and dependent on the listeners of the target future. If none of
289 // the target future's listeners want to have the stack trace we don't
290 // need a trace.
291 onError: (error, [stackTrace]) {
279 assert(target._isChained); 292 assert(target._isChained);
280 target._completeError(error); 293 target._completeError(error, stackTrace);
281 }); 294 });
282 } 295 }
283 } 296 }
284 297
285 void _complete(value) { 298 void _complete(value) {
286 assert(!_isComplete); 299 assert(!_isComplete);
287 assert(_onValue == null); 300 assert(_onValue == null);
288 assert(_onError == null); 301 assert(_onError == null);
289 assert(_whenCompleteAction == null); 302 assert(_whenCompleteAction == null);
290 assert(_errorTest == null); 303 assert(_errorTest == null);
(...skipping 13 matching lines...) Expand all
304 assert(_onError == null); 317 assert(_onError == null);
305 assert(_whenCompleteAction == null); 318 assert(_whenCompleteAction == null);
306 assert(_errorTest == null); 319 assert(_errorTest == null);
307 320
308 if (stackTrace != null) { 321 if (stackTrace != null) {
309 // Force the stack trace onto the error, even if it already had one. 322 // Force the stack trace onto the error, even if it already had one.
310 _attachStackTrace(error, stackTrace); 323 _attachStackTrace(error, stackTrace);
311 } 324 }
312 325
313 _Future listeners = _isChained ? null : _removeListeners(); 326 _Future listeners = _isChained ? null : _removeListeners();
314 _setError(error); 327 _setError(error, stackTrace);
315 _propagateToListeners(this, listeners); 328 _propagateToListeners(this, listeners);
316 } 329 }
317 330
318 void _asyncComplete(value) { 331 void _asyncComplete(value) {
319 assert(!_isComplete); 332 assert(!_isComplete);
320 assert(_onValue == null); 333 assert(_onValue == null);
321 assert(_onError == null); 334 assert(_onError == null);
322 assert(_whenCompleteAction == null); 335 assert(_whenCompleteAction == null);
323 assert(_errorTest == null); 336 assert(_errorTest == null);
324 // Two corner cases if the value is a future: 337 // Two corner cases if the value is a future:
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
391 * 404 *
392 * If [runCallback] is true (which should be the default) it executes 405 * If [runCallback] is true (which should be the default) it executes
393 * the registered action of listeners. If it is `false` then the callback is 406 * the registered action of listeners. If it is `false` then the callback is
394 * skipped. This is used to complete futures with chained futures. 407 * skipped. This is used to complete futures with chained futures.
395 */ 408 */
396 static void _propagateToListeners(_Future source, _Future listeners) { 409 static void _propagateToListeners(_Future source, _Future listeners) {
397 while (true) { 410 while (true) {
398 if (!source._isComplete) return; // Chained future. 411 if (!source._isComplete) return; // Chained future.
399 bool hasError = source._hasError; 412 bool hasError = source._hasError;
400 if (hasError && listeners == null) { 413 if (hasError && listeners == null) {
401 source._zone.handleUncaughtError(source._error); 414 _AsyncError asyncError = source._error;
415 source._zone.handleUncaughtError(
416 asyncError.error, asyncError.stackTrace);
402 return; 417 return;
403 } 418 }
404 if (listeners == null) return; 419 if (listeners == null) return;
405 _Future listener = listeners; 420 _Future listener = listeners;
406 if (listener._nextListener != null) { 421 if (listener._nextListener != null) {
407 // Usually futures only have one listener. If they have several, we 422 // Usually futures only have one listener. If they have several, we
408 // handle them specially. 423 // handle them specially.
409 _propagateMultipleListeners(source, listeners); 424 _propagateMultipleListeners(source, listeners);
410 return; 425 return;
411 } 426 }
412 if (hasError && !source._zone.inSameErrorZone(listener._zone)) { 427 if (hasError && !source._zone.inSameErrorZone(listener._zone)) {
413 // Don't cross zone boundaries with errors. 428 // Don't cross zone boundaries with errors.
414 source._zone.handleUncaughtError(source._error); 429 _AsyncError asyncError = source._error;
430 source._zone.handleUncaughtError(
431 asyncError.error, asyncError.stackTrace);
415 return; 432 return;
416 } 433 }
417 if (!identical(Zone.current, listener._zone)) { 434 if (!identical(Zone.current, listener._zone)) {
418 // Run the propagation in the listener's zone to avoid 435 // Run the propagation in the listener's zone to avoid
419 // zone transitions. The idea is that many chained futures will 436 // zone transitions. The idea is that many chained futures will
420 // be in the same zone. 437 // be in the same zone.
421 listener._zone.run(() { 438 listener._zone.run(() {
422 _propagateToListeners(source, listener); 439 _propagateToListeners(source, listener);
423 }); 440 });
424 return; 441 return;
(...skipping 22 matching lines...) Expand all
447 var value = source._value; 464 var value = source._value;
448 if (listener._onValue != null) { 465 if (listener._onValue != null) {
449 listenerValueOrError = listener._onValue(value); 466 listenerValueOrError = listener._onValue(value);
450 listenerHasValue = true; 467 listenerHasValue = true;
451 } else { 468 } else {
452 // Copy over the value from the source. 469 // Copy over the value from the source.
453 listenerValueOrError = value; 470 listenerValueOrError = value;
454 listenerHasValue = true; 471 listenerHasValue = true;
455 } 472 }
456 } else { 473 } else {
457 Object error = source._error; 474 _AsyncError asyncError = source._error;
458 _FutureErrorTest test = listener._errorTest; 475 _FutureErrorTest test = listener._errorTest;
459 bool matchesTest = true; 476 bool matchesTest = true;
460 if (test != null) { 477 if (test != null) {
461 matchesTest = test(error); 478 matchesTest = test(asyncError.error);
462 } 479 }
463 if (matchesTest && listener._onError != null) { 480 if (matchesTest && listener._onError != null) {
464 listenerValueOrError = listener._onError(error); 481 Function errorCallback = listener._onError;
482 if (errorCallback is ZoneBinaryCallback) {
483 listenerValueOrError =
484 errorCallback(asyncError.error, asyncError.stackTrace);
485 } else {
486 listenerValueOrError = listener._onError(asyncError.error);
487 }
465 listenerHasValue = true; 488 listenerHasValue = true;
466 } else { 489 } else {
467 // Copy over the error from the source. 490 // Copy over the error from the source.
468 listenerValueOrError = error; 491 listenerValueOrError = asyncError;
469 listenerHasValue = false; 492 listenerHasValue = false;
470 } 493 }
471 } 494 }
472 495
473 if (listener._whenCompleteAction != null) { 496 if (listener._whenCompleteAction != null) {
474 var completeResult = listener._whenCompleteAction(); 497 var completeResult = listener._whenCompleteAction();
475 if (completeResult is Future) { 498 if (completeResult is Future) {
476 listener._isChained = true; 499 listener._isChained = true;
477 completeResult.then((ignored) { 500 completeResult.then((ignored) {
478 // Try again, but this time don't run the whenComplete callback. 501 // Try again, but this time don't run the whenComplete callback.
479 _propagateToListeners(source, listener); 502 _propagateToListeners(source, listener);
480 }, onError: (error) { 503 }, onError: (error, [stackTrace]) {
481 // When there is an error, we have to make the error the new 504 // When there is an error, we have to make the error the new
482 // result of the current listener. 505 // result of the current listener.
483 if (completeResult is! _Future) { 506 if (completeResult is! _Future) {
484 // This should be a rare case. 507 // This should be a rare case.
485 completeResult = new _Future(); 508 completeResult = new _Future();
486 completeResult._setError(error); 509 completeResult._setError(error, stackTrace);
487 } 510 }
488 _propagateToListeners(completeResult, listener); 511 _propagateToListeners(completeResult, listener);
489 }); 512 });
490 isPropagationAborted = true; 513 isPropagationAborted = true;
491 } 514 }
492 } 515 }
493 } catch (e, s) { 516 } catch (e, s) {
494 // Set the exception as error. 517 // Set the exception as error unless the error is the same as the
495 listenerValueOrError = _asyncError(e, s); 518 // original one.
519 if (hasError && identical(source._error.error, e)) {
520 listenerValueOrError = source._error;
521 } else {
522 listenerValueOrError = new _AsyncError(_asyncError(e, s), s);
523 }
496 listenerHasValue = false; 524 listenerHasValue = false;
497 } 525 }
498 }); 526 });
499 if (isPropagationAborted) return; 527 if (isPropagationAborted) return;
500 // If the listener's value is a future we need to chain it. 528 // If the listener's value is a future we need to chain it.
501 if (listenerHasValue && listenerValueOrError is Future) { 529 if (listenerHasValue && listenerValueOrError is Future) {
502 Future chainSource = listenerValueOrError; 530 Future chainSource = listenerValueOrError;
503 // Shortcut if the chain-source is already completed. Just continue the 531 // Shortcut if the chain-source is already completed. Just continue the
504 // loop. 532 // loop.
505 if (chainSource is _Future && (chainSource as _Future)._isComplete) { 533 if (chainSource is _Future && (chainSource as _Future)._isComplete) {
506 // propagate the value (simulating a tail call). 534 // propagate the value (simulating a tail call).
507 listener._isChained = true; 535 listener._isChained = true;
508 source = chainSource; 536 source = chainSource;
509 listeners = listener; 537 listeners = listener;
510 continue; 538 continue;
511 } 539 }
512 _chainFutures(chainSource, listener); 540 _chainFutures(chainSource, listener);
513 return; 541 return;
514 } 542 }
515 543
516 if (listenerHasValue) { 544 if (listenerHasValue) {
517 listeners = listener._removeListeners(); 545 listeners = listener._removeListeners();
518 listener._setValue(listenerValueOrError); 546 listener._setValue(listenerValueOrError);
519 } else { 547 } else {
520 listeners = listener._removeListeners(); 548 listeners = listener._removeListeners();
521 listener._setError(listenerValueOrError); 549 _AsyncError asyncError = listenerValueOrError;
550 listener._setError(asyncError.error, asyncError.stackTrace);
522 } 551 }
523 // Prepare for next round. 552 // Prepare for next round.
524 source = listener; 553 source = listener;
525 } 554 }
526 } 555 }
527 } 556 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698