a1faa6f135
Review URL: https://codereview.chromium.org//10989013 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@12841 260f80e4-7a28-3924-810f-c04153c831b5
271 lines
5.9 KiB
Dart
271 lines
5.9 KiB
Dart
// 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);
|
|
}
|
|
}
|