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:
Lasse R.H. Nielsen
2017-03-31 12:41:04 +02:00
parent c5ffe74881
commit d509ddb3fa
3 changed files with 64 additions and 47 deletions
+36 -24
View File
@@ -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.
+25 -20
View File
@@ -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);
+3 -3
View File
@@ -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);