| // Copyright 2012 Google Inc. All Rights Reserved. | |
| // Dart core library. | |
| class FutureImpl<T> implements Future<T> { | |
| bool _isComplete = false; | |
| /** | |
| * Value that was provided to this Future by the Completer | |
| */ | |
| T _value; | |
| /** | |
| * Exception that occured, if there was a problem providing | |
| * Value. | |
| */ | |
| Object _exception; | |
| /** | |
| * Stack trace associated with [_exception], if one was provided. | |
| */ | |
| Object _stackTrace; | |
| /** | |
| * true, if any onException handler handled the exception. | |
| */ | |
| bool _exceptionHandled = false; | |
| /** | |
| * Listeners waiting to receive the value of this future. | |
| */ | |
| final List<Function> _successListeners; | |
| /** | |
| * Exception handlers waiting for exceptions. | |
| */ | |
| final List<Function> _exceptionHandlers; | |
| /** | |
| * Listeners waiting to be called when the future completes. | |
| */ | |
| final List<Function> _completionListeners; | |
| FutureImpl() | |
| : _successListeners = [], | |
| _exceptionHandlers = [], | |
| _completionListeners = []; | |
| factory FutureImpl.immediate(T value) { | |
| final res = new FutureImpl(); | |
| res._setValue(value); | |
| return res; | |
| } | |
| T get value { | |
| if (!isComplete) { | |
| throw new FutureNotCompleteException(); | |
| } | |
| if (_exception !== null) { | |
| throw _exception; | |
| } | |
| return _value; | |
| } | |
| Object get exception { | |
| if (!isComplete) { | |
| throw new FutureNotCompleteException(); | |
| } | |
| return _exception; | |
| } | |
| Object get stackTrace { | |
| if (!isComplete) { | |
| throw new FutureNotCompleteException(); | |
| } | |
| return _stackTrace; | |
| } | |
| bool get isComplete { | |
| return _isComplete; | |
| } | |
| bool get hasValue { | |
| return isComplete && _exception === null; | |
| } | |
| void then(void onSuccess(T value)) { | |
| if (hasValue) { | |
| onSuccess(value); | |
| } else if (!isComplete) { | |
| _successListeners.add(onSuccess); | |
| } else if (!_exceptionHandled) { | |
| throw _exception; | |
| } | |
| } | |
| void handleException(bool onException(Object exception)) { | |
| if (_exceptionHandled) return; | |
| if (_isComplete) { | |
| if (_exception != null) { | |
| _exceptionHandled = onException(_exception); | |
| } | |
| } else { | |
| _exceptionHandlers.add(onException); | |
| } | |
| } | |
| void onComplete(void complete(Future<T> future)) { | |
| if (_isComplete) { | |
| try { | |
| complete(this); | |
| } catch (e) {} | |
| } else { | |
| _completionListeners.add(complete); | |
| } | |
| } | |
| void _complete() { | |
| _isComplete = true; | |
| try { | |
| if (_exception !== null) { | |
| for (Function handler in _exceptionHandlers) { | |
| // Explicitly check for true here so that if the handler returns null, | |
| // we don't get an exception in checked mode. | |
| if (handler(_exception) == true) { | |
| _exceptionHandled = true; | |
| break; | |
| } | |
| } | |
| } | |
| if (hasValue) { | |
| for (Function listener in _successListeners) { | |
| listener(value); | |
| } | |
| } else { | |
| if (!_exceptionHandled && _successListeners.length > 0) { | |
| throw _exception; | |
| } | |
| } | |
| } finally { | |
| for (Function listener in _completionListeners) { | |
| try { | |
| listener(this); | |
| } catch (e) {} | |
| } | |
| } | |
| } | |
| void _setValue(T value) { | |
| if (_isComplete) { | |
| throw new FutureAlreadyCompleteException(); | |
| } | |
| _value = value; | |
| _complete(); | |
| } | |
| void _setException(Object exception, Object stackTrace) { | |
| if (exception === null) { | |
| // null is not a legal value for the exception of a Future. | |
| throw new ArgumentError(null); | |
| } | |
| if (_isComplete) { | |
| throw new FutureAlreadyCompleteException(); | |
| } | |
| _exception = exception; | |
| _stackTrace = stackTrace; | |
| _complete(); | |
| } | |
| Future transform(Function transformation) { | |
| final completer = new Completer(); | |
| _forwardException(this, completer); | |
| then((v) { | |
| var transformed = null; | |
| try { | |
| transformed = transformation(v); | |
| } catch (e, stackTrace) { | |
| completer.completeException(e, stackTrace); | |
| return; | |
| } | |
| completer.complete(transformed); | |
| }); | |
| return completer.future; | |
| } | |
| Future chain(Function transformation) { | |
| final completer = new Completer(); | |
| _forwardException(this, completer); | |
| then((v) { | |
| var future = null; | |
| try { | |
| future = transformation(v); | |
| } catch (ex, stackTrace) { | |
| completer.completeException(ex, stackTrace); | |
| return; | |
| } | |
| _forward(future, completer); | |
| }); | |
| return completer.future; | |
| } | |
| Future transformException(transformation(Object exception)) { | |
| final completer = new Completer(); | |
| handleException((ex) { | |
| try { | |
| final result = transformation(ex); | |
| // If the transformation itself returns a future, then we will | |
| // complete to what that completes to. | |
| if (result is Future) { | |
| _forward(result, completer); | |
| } else { | |
| completer.complete(result); | |
| } | |
| } catch (innerException, stackTrace) { | |
| completer.completeException(innerException, stackTrace); | |
| } | |
| return true; | |
| }); | |
| then(completer.complete); | |
| return completer.future; | |
| } | |
| /** | |
| * Forwards the success or error completion from [future] to [completer]. | |
| */ | |
| _forward(Future future, Completer completer) { | |
| _forwardException(future, completer); | |
| future.then(completer.complete); | |
| } | |
| /** | |
| * Forwards the exception completion from [future] to [completer]. | |
| */ | |
| _forwardException(Future future, Completer completer) { | |
| future.handleException((e) { | |
| completer.completeException(e, future.stackTrace); | |
| return true; | |
| }); | |
| } | |
| } | |
| class CompleterImpl<T> implements Completer<T> { | |
| final FutureImpl<T> _futureImpl; | |
| CompleterImpl() : _futureImpl = new FutureImpl() {} | |
| Future<T> get future { | |
| return _futureImpl; | |
| } | |
| void complete(T value) { | |
| _futureImpl._setValue(value); | |
| } | |
| void completeException(Object exception, [Object stackTrace]) { | |
| _futureImpl._setException(exception, stackTrace); | |
| } | |
| } |