Use FutureOr more and make Future.sync return the resulting Future directly.
When the computation passed to Future.sync returns a Future of the correct type, then it's returned directly instead of wrapping it again. (Until strong mode, we have an extra case for when it returns a Future of an incorrect type, but that will eventually be removed). This should improve the performance of Future.sync a bit. Also adds missing Zone intercept for when the Future.sync computation throws. Updates documentation for Future.doWhile. Fixes #29202 BUG= http://dartbug.com/29202 R=floitsch@google.com Review-Url: https://codereview.chromium.org/2790663003 .
This commit is contained in:
+36
-24
@@ -37,7 +37,7 @@ part of dart.async;
|
||||
/// `FutureOr<FutureOr<Object>>`, `FutureOr<Future<Object>> is equivalent to
|
||||
/// `Future<Object>`.
|
||||
abstract class FutureOr<T> {
|
||||
// Private constructor, so that it is not subclassable, mixable, or
|
||||
// Private generative constructor, so that it is not subclassable, mixable, or
|
||||
// instantiable.
|
||||
FutureOr._() {
|
||||
throw new UnsupportedError("FutureOr can't be instantiated");
|
||||
@@ -151,7 +151,7 @@ abstract class Future<T> {
|
||||
* If a non-future value is returned, the returned future is completed
|
||||
* with that value.
|
||||
*/
|
||||
factory Future(computation()) {
|
||||
factory Future(FutureOr<T> computation()) {
|
||||
_Future<T> result = new _Future<T>();
|
||||
Timer.run(() {
|
||||
try {
|
||||
@@ -177,7 +177,7 @@ abstract class Future<T> {
|
||||
* If calling [computation] returns a non-future value,
|
||||
* the returned future is completed with that value.
|
||||
*/
|
||||
factory Future.microtask(computation()) {
|
||||
factory Future.microtask(FutureOr<T> computation()) {
|
||||
_Future<T> result = new _Future<T>();
|
||||
scheduleMicrotask(() {
|
||||
try {
|
||||
@@ -190,38 +190,51 @@ abstract class Future<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a future containing the result of immediately calling
|
||||
* Returns a future containing the result of immediately calling
|
||||
* [computation].
|
||||
*
|
||||
* If calling [computation] throws, the returned future is completed with the
|
||||
* error.
|
||||
*
|
||||
* If calling [computation] returns a [Future], completion of
|
||||
* the created future will wait until the returned future completes,
|
||||
* and will then complete with the same result.
|
||||
* If calling [computation] returns a `Future<T>`, that future is returned.
|
||||
*
|
||||
* If calling [computation] returns a non-future value,
|
||||
* the returned future is completed with that value.
|
||||
* a future is returned which has been completed with that value.
|
||||
*/
|
||||
factory Future.sync(computation()) {
|
||||
factory Future.sync(FutureOr<T> computation()) {
|
||||
try {
|
||||
var result = computation();
|
||||
return new Future<T>.value(result);
|
||||
if (result is Future<T>) {
|
||||
return result;
|
||||
} else if (result is Future) {
|
||||
// TODO(lrn): Remove this case for Dart 2.0.
|
||||
return new _Future<T>.immediate(result);
|
||||
} else {
|
||||
return new _Future<T>.value(result);
|
||||
}
|
||||
} catch (error, stackTrace) {
|
||||
return new Future<T>.error(error, stackTrace);
|
||||
var future = new _Future<T>();
|
||||
AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
|
||||
if (replacement != null) {
|
||||
future._asyncCompleteError(
|
||||
_nonNullError(replacement.error), replacement.stackTrace);
|
||||
} else {
|
||||
future._asyncCompleteError(error, stackTrace);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A future whose value is available in the next event-loop iteration.
|
||||
*
|
||||
* If [value] is not a [Future], using this constructor is equivalent
|
||||
* to [:new Future<T>.sync(() => value):].
|
||||
* If [result] is not a [Future], using this constructor is equivalent
|
||||
* to `new Future<T>.sync(() => result)`.
|
||||
*
|
||||
* Use [Completer] to create a Future and complete it later.
|
||||
* Use [Completer] to create a future and complete it later.
|
||||
*/
|
||||
factory Future.value([value]) {
|
||||
return new _Future<T>.immediate(value);
|
||||
factory Future.value([FutureOr<T> result]) {
|
||||
return new _Future<T>.immediate(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,7 +435,7 @@ abstract class Future<T> {
|
||||
* If [f] returns a non-[Future], iteration continues immediately. Otherwise
|
||||
* it waits for the returned [Future] to complete.
|
||||
*/
|
||||
static Future forEach<T>(Iterable<T> input, dynamic f(T element)) {
|
||||
static Future forEach<T>(Iterable<T> input, FutureOr f(T element)) {
|
||||
var iterator = input.iterator;
|
||||
return doWhile(() {
|
||||
if (!iterator.moveNext()) return false;
|
||||
@@ -437,13 +450,12 @@ abstract class Future<T> {
|
||||
* value `true` or a [Future] which completes with the value `true`.
|
||||
*
|
||||
* If a call to [f] returns `false` or a [Future] that completes to `false`,
|
||||
* iteration ends and the future returned by [doWhile] is completed.
|
||||
* iteration ends and the future returned by [doWhile] is completed with
|
||||
* a `null` value.
|
||||
*
|
||||
* If a future returned by [f] completes with an error, iteration ends and
|
||||
* the future returned by [doWhile] completes with the same error.
|
||||
*
|
||||
* The [f] function must return either a `bool` value or a [Future] completing
|
||||
* with a `bool` value.
|
||||
* If a call to [f] throws or a future returned by [f] completes with
|
||||
* an error, iteration ends and the future returned by [doWhile]
|
||||
* completes with the same error.
|
||||
*/
|
||||
static Future doWhile(FutureOr<bool> f()) {
|
||||
_Future doneSignal = new _Future();
|
||||
@@ -609,7 +621,7 @@ abstract class Future<T> {
|
||||
* });
|
||||
* }
|
||||
*/
|
||||
Future<T> whenComplete(dynamic action());
|
||||
Future<T> whenComplete(FutureOr action());
|
||||
|
||||
/**
|
||||
* Creates a [Stream] containing the result of this future.
|
||||
|
||||
@@ -207,15 +207,19 @@ class _Future<T> implements Future<T> {
|
||||
// This constructor is used by async/await.
|
||||
_Future();
|
||||
|
||||
/// Valid types for value: `T` or `Future<T>`.
|
||||
_Future.immediate(value) {
|
||||
_asyncComplete(value);
|
||||
_Future.immediate(FutureOr<T> result) {
|
||||
_asyncComplete(result);
|
||||
}
|
||||
|
||||
_Future.immediateError(var error, [StackTrace stackTrace]) {
|
||||
_asyncCompleteError(error, stackTrace);
|
||||
}
|
||||
|
||||
/** Creates a future that is already completed with the value. */
|
||||
_Future.value(T value) {
|
||||
_setValue(value);
|
||||
}
|
||||
|
||||
bool get _mayComplete => _state == _INCOMPLETE;
|
||||
bool get _isPendingComplete => _state == _PENDING_COMPLETE;
|
||||
bool get _mayAddListener => _state <= _PENDING_COMPLETE;
|
||||
@@ -496,23 +500,7 @@ class _Future<T> implements Future<T> {
|
||||
// it.
|
||||
|
||||
if (value is Future<T>) {
|
||||
if (value is _Future<T>) {
|
||||
if (value._hasError) {
|
||||
// Case 1 from above. Delay completion to enable the user to register
|
||||
// callbacks.
|
||||
_setPendingComplete();
|
||||
_zone.scheduleMicrotask(() {
|
||||
_chainCoreFuture(value, this);
|
||||
});
|
||||
} else {
|
||||
_chainCoreFuture(value, this);
|
||||
}
|
||||
} else {
|
||||
// Case 2 from above. Chain the future immediately.
|
||||
// Note that we are still completing asynchronously (through
|
||||
// _chainForeignFuture).
|
||||
_chainForeignFuture(value, this);
|
||||
}
|
||||
_chainFuture(value);
|
||||
return;
|
||||
}
|
||||
T typedValue = value as Object/*=T*/;
|
||||
@@ -523,6 +511,23 @@ class _Future<T> implements Future<T> {
|
||||
});
|
||||
}
|
||||
|
||||
void _chainFuture(Future<T> value) {
|
||||
if (value is _Future<T>) {
|
||||
if (value._hasError) {
|
||||
// Delay completion to allow the user to register callbacks.
|
||||
_setPendingComplete();
|
||||
_zone.scheduleMicrotask(() {
|
||||
_chainCoreFuture(value, this);
|
||||
});
|
||||
} else {
|
||||
_chainCoreFuture(value, this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Just listen on the foreign future. This guarantees an async delay.
|
||||
_chainForeignFuture(value, this);
|
||||
}
|
||||
|
||||
void _asyncCompleteError(error, StackTrace stackTrace) {
|
||||
assert(!_isComplete);
|
||||
|
||||
|
||||
@@ -994,9 +994,9 @@ void testTypes() {
|
||||
new Future<int>.delayed(Duration.ZERO, () => value));
|
||||
testType(
|
||||
"Future.microtask($value)", new Future<int>.microtask(() => value));
|
||||
testType("Future.sync($value)", new Future<int>.sync(() => value)); // //# 01: ok
|
||||
testType("Future.sync(future($value))", // //# 01: continued
|
||||
new Future<int>.sync(() async => new Future.value(value))); //# 01: continued
|
||||
testType("Future.sync($value)", new Future<int>.sync(() => value)); // //# 01: ok
|
||||
testType("Future.sync(future($value))", // //# 01: continued
|
||||
new Future<int>.sync(() async => new Future<int>.value(value))); //# 01: continued
|
||||
testType("Future.value($value)", new Future<int>.value(value));
|
||||
}
|
||||
testType("Completer.future", new Completer<int>().future);
|
||||
|
||||
Reference in New Issue
Block a user