Add record-related wait extensions on Future tuples.

With the addition of records, it now makes sense to create an API
for parallelizing of futures of different types, like `Future.wait`
does for collections of same-typed futures.

The `wait` getters here apply to tuples of 2-9 futures of distinct types, which should be enough for most reasonable uses.

Planned addition for Dart 3.0.



CoreLibraryReviewExempt: Everybody's on vacation, everybody everywhere.
Change-Id: Iaa814e02e2274082bb8a29b9a18b4930bcc953bb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/288903
Reviewed-by: Nate Bosch <nbosch@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Lasse Nielsen <lrn@google.com>
This commit is contained in:
Lasse R.H. Nielsen 2023-04-04 11:39:49 +00:00 committed by Lasse Nielsen
parent 794c89415b
commit 2f7213342b
16 changed files with 1151 additions and 8 deletions

View file

@ -121,6 +121,8 @@
#### `dart:async`
- Added extension member `wait` on iterables and 2-9 tuples of futures.
- **Breaking change** [#49529][]:
- Removed the deprecated [`DeferredLibrary`][] class.
Use the [`deferred as`][] import syntax instead.

View file

@ -160,7 +160,17 @@ additionalExports = (core::Deprecated,
core::override,
asy::Future,
asy::Stream,
asy::FutureExtensions)
asy::FutureExtensions,
asy::FutureIterable,
asy::FutureRecord2,
asy::FutureRecord3,
asy::FutureRecord4,
asy::FutureRecord5,
asy::FutureRecord6,
asy::FutureRecord7,
asy::FutureRecord8,
asy::FutureRecord9,
asy::ParallelWaitError)
export "dart:core";
export "org-dartlang-testcase:///main_lib2.dart";

View file

@ -156,7 +156,17 @@ additionalExports = (core::Deprecated,
core::override,
asy::Future,
asy::Stream,
asy::FutureExtensions)
asy::FutureExtensions,
asy::FutureIterable,
asy::FutureRecord2,
asy::FutureRecord3,
asy::FutureRecord4,
asy::FutureRecord5,
asy::FutureRecord6,
asy::FutureRecord7,
asy::FutureRecord8,
asy::FutureRecord9,
asy::ParallelWaitError)
export "dart:core";
export "org-dartlang-testcase:///main_lib2.dart";

View file

@ -160,7 +160,17 @@ additionalExports = (core::Deprecated,
core::override,
asy::Future,
asy::Stream,
asy::FutureExtensions)
asy::FutureExtensions,
asy::FutureIterable,
asy::FutureRecord2,
asy::FutureRecord3,
asy::FutureRecord4,
asy::FutureRecord5,
asy::FutureRecord6,
asy::FutureRecord7,
asy::FutureRecord8,
asy::FutureRecord9,
asy::ParallelWaitError)
export "dart:core";
export "org-dartlang-testcase:///main_lib2.dart";

View file

@ -95,7 +95,17 @@ additionalExports = (core::Deprecated,
core::override,
asy::Future,
asy::Stream,
asy::FutureExtensions)
asy::FutureExtensions,
asy::FutureIterable,
asy::FutureRecord2,
asy::FutureRecord3,
asy::FutureRecord4,
asy::FutureRecord5,
asy::FutureRecord6,
asy::FutureRecord7,
asy::FutureRecord8,
asy::FutureRecord9,
asy::ParallelWaitError)
export "dart:core";
export "org-dartlang-testcase:///main_lib2.dart";

View file

@ -156,7 +156,17 @@ additionalExports = (core::Deprecated,
core::override,
asy::Future,
asy::Stream,
asy::FutureExtensions)
asy::FutureExtensions,
asy::FutureIterable,
asy::FutureRecord2,
asy::FutureRecord3,
asy::FutureRecord4,
asy::FutureRecord5,
asy::FutureRecord6,
asy::FutureRecord7,
asy::FutureRecord8,
asy::FutureRecord9,
asy::ParallelWaitError)
export "dart:core";
export "org-dartlang-testcase:///main_lib2.dart";

View file

@ -2,6 +2,16 @@ main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
additionalExports = (asy::Future,
asy::FutureExtensions,
asy::FutureIterable,
asy::FutureRecord2,
asy::FutureRecord3,
asy::FutureRecord4,
asy::FutureRecord5,
asy::FutureRecord6,
asy::FutureRecord7,
asy::FutureRecord8,
asy::FutureRecord9,
asy::ParallelWaitError,
asy::Stream,
core::deprecated,
core::override,

View file

@ -2,6 +2,16 @@ main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
additionalExports = (asy::Future,
asy::FutureExtensions,
asy::FutureIterable,
asy::FutureRecord2,
asy::FutureRecord3,
asy::FutureRecord4,
asy::FutureRecord5,
asy::FutureRecord6,
asy::FutureRecord7,
asy::FutureRecord8,
asy::FutureRecord9,
asy::ParallelWaitError,
asy::Stream,
core::deprecated,
core::override,

View file

@ -59,7 +59,7 @@ main() async {
if (uri == '') {
// We don't verify non-user-visible objects.
} else if (uri.startsWith('dart') &&
['Array', 'List'].any((p) => klass.name.contains(p))) {
['Array', 'List', 'Record'].any((p) => klass.name.contains(p))) {
Expect.isTrue(fields.length <= object.references.length);
} else {
Expect.equals(fields.length, object.references.length);

View file

@ -61,7 +61,7 @@ main() async {
if (uri == '') {
// We don't verify non-user-visible objects.
} else if (uri.startsWith('dart') &&
['Array', 'List'].any((p) => klass.name.contains(p))) {
['Array', 'List', 'Record'].any((p) => klass.name.contains(p))) {
Expect.isTrue(fields.length <= object.references.length);
} else {
Expect.equals(fields.length, object.references.length);

View file

@ -109,7 +109,6 @@ import "dart:_internal"
CastStream,
CastStreamTransformer,
checkNotNullable,
EmptyIterator,
IterableElementError,
nullFuture,
printToZone,
@ -123,6 +122,7 @@ part 'async_error.dart';
part 'broadcast_stream_controller.dart';
part 'deferred_load.dart';
part 'future.dart';
part 'future_extensions.dart';
part 'future_impl.dart';
part 'schedule_microtask.dart';
part 'stream.dart';

View file

@ -11,6 +11,7 @@ async_sdk_sources = [
"broadcast_stream_controller.dart",
"deferred_load.dart",
"future.dart",
"future_extensions.dart",
"future_impl.dart",
"schedule_microtask.dart",
"stream.dart",

View file

@ -0,0 +1,537 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.async;
@Since("3.0")
extension FutureIterable<T> on Iterable<Future<T>> {
/// Waits for futures in parallel.
///
/// Waits for all the futures in this iterable.
/// Returns a list of the resulting values,
/// in the same order as the futures which created them,
/// if all futures are successful.
///
/// Similar to [Future.wait], but reports errors using a
/// [ParallelWaitError], which allows the caller to
/// handle errors and dispose successful results if necessary.
///
/// The returned future is completed when all the futures have completed.
/// If any of the futures do not complete, nor does the returned future.
///
/// If any future completes with an error,
/// the returned future completes with a [ParallelWaitError].
/// The [ParallelWaitError.values] is a list of the values for
/// successful futures and `null` for futures with errors.
/// The [ParallelWaitError.errors] is a list of the same length,
/// with `null` values for the successful futures
/// and an [AsyncError] with the error for futures
/// which completed with an error.
Future<List<T>> get wait {
var results = [for (var f in this) _FutureResult<T>(f)];
if (results.isEmpty) return Future<List<T>>.value(<T>[]);
final c = Completer<List<T>>.sync();
_FutureResult._waitAll(results, (errors) {
if (errors == 0) {
c.complete([for (var r in results) r.value]);
} else {
c.completeError(ParallelWaitError<List<T?>, List<AsyncError?>>(
[for (var r in results) r.valueOrNull],
[for (var r in results) r.errorOrNull],
));
}
});
return c.future;
}
}
/// Parallel operations on a record of futures.
///
/// {@template record-parallel-wait}
/// Waits for futures in parallel.
///
/// Waits for all the futures in this record.
/// Returns a record of the values, if all futures are successful.
///
/// The returned future is completed when all the futures have completed.
/// If any of the futures do not complete, nor does the returned future.
///
/// If some futures complete with an error,
/// the returned future completes with a [ParallelWaitError].
/// The [ParallelWaitError.values] is a record of the values of
/// successful futures, and `null` for futures with errors.
/// The [ParallelWaitError.errors] is a record of the same shape,
/// with `null` values for the successful futures
/// and an [AsyncError] with the error of futures
/// which completed with an error.
/// {@endtemplate}
@Since("3.0")
extension FutureRecord2<T1, T2> on (Future<T1>, Future<T2>) {
/// {@macro record-parallel-wait}
Future<(T1, T2)> get wait {
final c = Completer<(T1, T2)>.sync();
final v1 = _FutureResult<T1>($1);
final v2 = _FutureResult<T2>($2);
_FutureResult._waitAll([v1, v2], (int errors) {
if (errors == 0) {
c.complete((v1.value, v2.value));
} else {
c.completeError(ParallelWaitError(
(v1.valueOrNull, v2.valueOrNull),
(v1.errorOrNull, v2.errorOrNull),
));
}
});
return c.future;
}
}
/// Parallel operations on a record of futures.
@Since("3.0")
extension FutureRecord3<T1, T2, T3> on (Future<T1>, Future<T2>, Future<T3>) {
/// {@macro record-parallel-wait}
Future<(T1, T2, T3)> get wait {
final c = Completer<(T1, T2, T3)>.sync();
final v1 = _FutureResult<T1>($1);
final v2 = _FutureResult<T2>($2);
final v3 = _FutureResult<T3>($3);
_FutureResult._waitAll([v1, v2, v3], (int errors) {
if (errors == 0) {
c.complete((v1.value, v2.value, v3.value));
} else {
c.completeError(ParallelWaitError(
(v1.valueOrNull, v2.valueOrNull, v3.valueOrNull),
(v1.errorOrNull, v2.errorOrNull, v3.errorOrNull),
));
}
});
return c.future;
}
}
/// Parallel operations on a record of futures.
@Since("3.0")
extension FutureRecord4<T1, T2, T3, T4> on (
Future<T1>,
Future<T2>,
Future<T3>,
Future<T4>
) {
/// {@macro record-parallel-wait}
Future<(T1, T2, T3, T4)> get wait {
final c = Completer<(T1, T2, T3, T4)>.sync();
final v1 = _FutureResult<T1>($1);
final v2 = _FutureResult<T2>($2);
final v3 = _FutureResult<T3>($3);
final v4 = _FutureResult<T4>($4);
_FutureResult._waitAll([v1, v2, v3, v4], (int errors) {
if (errors == 0) {
c.complete((v1.value, v2.value, v3.value, v4.value));
} else {
c.completeError(ParallelWaitError(
(v1.valueOrNull, v2.valueOrNull, v3.valueOrNull, v4.valueOrNull),
(v1.errorOrNull, v2.errorOrNull, v3.errorOrNull, v4.errorOrNull),
));
}
});
return c.future;
}
}
/// Parallel operations on a record of futures.
@Since("3.0")
extension FutureRecord5<T1, T2, T3, T4, T5> on (
Future<T1>,
Future<T2>,
Future<T3>,
Future<T4>,
Future<T5>
) {
/// {@macro record-parallel-wait}
Future<(T1, T2, T3, T4, T5)> get wait {
final c = Completer<(T1, T2, T3, T4, T5)>.sync();
final v1 = _FutureResult<T1>($1);
final v2 = _FutureResult<T2>($2);
final v3 = _FutureResult<T3>($3);
final v4 = _FutureResult<T4>($4);
final v5 = _FutureResult<T5>($5);
_FutureResult._waitAll([v1, v2, v3, v4, v5], (int errors) {
if (errors == 0) {
c.complete((v1.value, v2.value, v3.value, v4.value, v5.value));
} else {
c.completeError(ParallelWaitError(
(
v1.valueOrNull,
v2.valueOrNull,
v3.valueOrNull,
v4.valueOrNull,
v5.valueOrNull
),
(
v1.errorOrNull,
v2.errorOrNull,
v3.errorOrNull,
v4.errorOrNull,
v5.errorOrNull
),
));
}
});
return c.future;
}
}
/// Parallel operations on a record of futures.
@Since("3.0")
extension FutureRecord6<T1, T2, T3, T4, T5, T6> on (
Future<T1>,
Future<T2>,
Future<T3>,
Future<T4>,
Future<T5>,
Future<T6>
) {
/// {@macro record-parallel-wait}
Future<(T1, T2, T3, T4, T5, T6)> get wait {
final c = Completer<(T1, T2, T3, T4, T5, T6)>.sync();
final v1 = _FutureResult<T1>($1);
final v2 = _FutureResult<T2>($2);
final v3 = _FutureResult<T3>($3);
final v4 = _FutureResult<T4>($4);
final v5 = _FutureResult<T5>($5);
final v6 = _FutureResult<T6>($6);
_FutureResult._waitAll([v1, v2, v3, v4, v5, v6], (int errors) {
if (errors == 0) {
c.complete(
(v1.value, v2.value, v3.value, v4.value, v5.value, v6.value));
} else {
c.completeError(ParallelWaitError(
(
v1.valueOrNull,
v2.valueOrNull,
v3.valueOrNull,
v4.valueOrNull,
v5.valueOrNull,
v6.valueOrNull
),
(
v1.errorOrNull,
v2.errorOrNull,
v3.errorOrNull,
v4.errorOrNull,
v5.errorOrNull,
v6.errorOrNull
),
));
}
});
return c.future;
}
}
/// Parallel operations on a record of futures.
@Since("3.0")
extension FutureRecord7<T1, T2, T3, T4, T5, T6, T7> on (
Future<T1>,
Future<T2>,
Future<T3>,
Future<T4>,
Future<T5>,
Future<T6>,
Future<T7>
) {
/// {@macro record-parallel-wait}
Future<(T1, T2, T3, T4, T5, T6, T7)> get wait {
final c = Completer<(T1, T2, T3, T4, T5, T6, T7)>.sync();
final v1 = _FutureResult<T1>($1);
final v2 = _FutureResult<T2>($2);
final v3 = _FutureResult<T3>($3);
final v4 = _FutureResult<T4>($4);
final v5 = _FutureResult<T5>($5);
final v6 = _FutureResult<T6>($6);
final v7 = _FutureResult<T7>($7);
_FutureResult._waitAll([v1, v2, v3, v4, v5, v6, v7], (int errors) {
if (errors == 0) {
c.complete(
(
v1.value,
v2.value,
v3.value,
v4.value,
v5.value,
v6.value,
v7.value
));
} else {
c.completeError(ParallelWaitError(
(
v1.valueOrNull,
v2.valueOrNull,
v3.valueOrNull,
v4.valueOrNull,
v5.valueOrNull,
v6.valueOrNull,
v7.valueOrNull
),
(
v1.errorOrNull,
v2.errorOrNull,
v3.errorOrNull,
v4.errorOrNull,
v5.errorOrNull,
v6.errorOrNull,
v7.errorOrNull
),
));
}
});
return c.future;
}
}
/// Parallel operations on a record of futures.
@Since("3.0")
extension FutureRecord8<T1, T2, T3, T4, T5, T6, T7, T8> on (
Future<T1>,
Future<T2>,
Future<T3>,
Future<T4>,
Future<T5>,
Future<T6>,
Future<T7>,
Future<T8>
) {
/// {@macro record-parallel-wait}
Future<(T1, T2, T3, T4, T5, T6, T7, T8)> get wait {
final c = Completer<(T1, T2, T3, T4, T5, T6, T7, T8)>.sync();
final v1 = _FutureResult<T1>($1);
final v2 = _FutureResult<T2>($2);
final v3 = _FutureResult<T3>($3);
final v4 = _FutureResult<T4>($4);
final v5 = _FutureResult<T5>($5);
final v6 = _FutureResult<T6>($6);
final v7 = _FutureResult<T7>($7);
final v8 = _FutureResult<T8>($8);
_FutureResult._waitAll([v1, v2, v3, v4, v5, v6, v7, v8], (int errors) {
if (errors == 0) {
c.complete(
(
v1.value,
v2.value,
v3.value,
v4.value,
v5.value,
v6.value,
v7.value,
v8.value
));
} else {
c.completeError(ParallelWaitError(
(
v1.valueOrNull,
v2.valueOrNull,
v3.valueOrNull,
v4.valueOrNull,
v5.valueOrNull,
v6.valueOrNull,
v7.valueOrNull,
v8.valueOrNull
),
(
v1.errorOrNull,
v2.errorOrNull,
v3.errorOrNull,
v4.errorOrNull,
v5.errorOrNull,
v6.errorOrNull,
v7.errorOrNull,
v8.errorOrNull
),
));
}
});
return c.future;
}
}
/// Parallel operations on a record of futures.
@Since("3.0")
extension FutureRecord9<T1, T2, T3, T4, T5, T6, T7, T8, T9> on (
Future<T1>,
Future<T2>,
Future<T3>,
Future<T4>,
Future<T5>,
Future<T6>,
Future<T7>,
Future<T8>,
Future<T9>
) {
/// {@macro record-parallel-wait}
Future<(T1, T2, T3, T4, T5, T6, T7, T8, T9)> get wait {
final c = Completer<(T1, T2, T3, T4, T5, T6, T7, T8, T9)>.sync();
final v1 = _FutureResult<T1>($1);
final v2 = _FutureResult<T2>($2);
final v3 = _FutureResult<T3>($3);
final v4 = _FutureResult<T4>($4);
final v5 = _FutureResult<T5>($5);
final v6 = _FutureResult<T6>($6);
final v7 = _FutureResult<T7>($7);
final v8 = _FutureResult<T8>($8);
final v9 = _FutureResult<T9>($9);
_FutureResult._waitAll([v1, v2, v3, v4, v5, v6, v7, v8, v9], (int errors) {
if (errors == 0) {
c.complete(
(
v1.value,
v2.value,
v3.value,
v4.value,
v5.value,
v6.value,
v7.value,
v8.value,
v9.value
));
} else {
c.completeError(ParallelWaitError(
(
v1.valueOrNull,
v2.valueOrNull,
v3.valueOrNull,
v4.valueOrNull,
v5.valueOrNull,
v6.valueOrNull,
v7.valueOrNull,
v8.valueOrNull,
v9.valueOrNull
),
(
v1.errorOrNull,
v2.errorOrNull,
v3.errorOrNull,
v4.errorOrNull,
v5.errorOrNull,
v6.errorOrNull,
v7.errorOrNull,
v8.errorOrNull,
v9.errorOrNull
),
));
}
});
return c.future;
}
}
/// Error thrown when waiting for multiple futures, when some have errors.
///
/// The [V] and [E] types will have the same basic shape as the
/// original collection of futures that was waited on.
///
/// For example, if the original awaited futures were a record
/// `(Future<T1>, ..., Future<Tn>)`,
/// the type `V` will be `(T1?, ..., Tn?)` which allows keeping the
/// values of futures that completed with a value,
/// and `E` will be `(AsyncError?, ..., AsyncError?)`, also with *n*
/// fields, which can contain the errors for the futures which completed
/// with an error.
///
/// Waiting for a list or iterable of futures should provide
/// a list of nullable values and errors of the same length.
@Since("3.0")
class ParallelWaitError<V, E> extends Error {
/// Values of successful futures.
///
/// Has the same shape as the original collection of futures,
/// with values for each successful future and `null` values
/// for each failing future.
final V values;
/// Errors of failing futures.
///
/// Has the same shape as the original collection of futures,
/// with errors, typically [AsyncError], for each failing
/// future and `null` values for each successful future.
final E errors;
/// Creates error with the provided [values] and [errors].
ParallelWaitError(this.values, this.errors);
String toString() => "ParallelWaitError";
}
/// The result of a future, when it has completed.
///
/// Stores a value result in [value] and an error result in [error].
/// Then calls [onReady] with a 0 argument for a value, and 1 for an error.
///
/// The [onReady] callback must be set synchronously,
/// before the future has a chance to complete.
///
/// Abstracted into a class of its own in order to reuse code.
class _FutureResult<T> {
// Consider integrating directly into `_Future` as a `_FutureListener`
// to avoid creating as many function tear-offs.
/// The value or `null`.
///
/// Set when the future completes with a value.
T? valueOrNull;
/// Set when the future completes with an error or value.
AsyncError? errorOrNull;
void Function(int errors) onReady = _noop;
_FutureResult(Future<T> future) {
future.then(_onValue, onError: _onError);
}
/// The value.
///
/// Should only be used when the future is known to have completed with
/// a value.
T get value => valueOrNull ?? valueOrNull as T;
void _onValue(T value) {
valueOrNull = value;
onReady(0);
}
void _onError(Object error, StackTrace stack) {
this.errorOrNull = AsyncError(error, stack);
onReady(1);
}
/// Waits for a number of [_FutureResult]s to all have completed.
///
/// List must not be empty.
static void _waitAll(
List<_FutureResult> results, void Function(int) whenReady) {
assert(results.isNotEmpty);
var ready = 0;
var errors = 0;
void onReady(int error) {
errors += error;
if (++ready == results.length) {
whenReady(errors);
}
}
for (var r in results) {
r.onReady = onReady;
}
}
static void _noop(_) {}
}

View file

@ -169,6 +169,19 @@ import "dart:typed_data" show Uint8List;
export "dart:async" show Future, Stream;
@Since("2.12")
export "dart:async" show FutureExtensions;
@Since("3.0")
export "dart:async"
show
FutureIterable,
FutureRecord2,
FutureRecord3,
FutureRecord4,
FutureRecord5,
FutureRecord6,
FutureRecord7,
FutureRecord8,
FutureRecord9,
ParallelWaitError;
part "annotations.dart";
part "bigint.dart";

View file

@ -0,0 +1,79 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:async";
import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import '../../language/static_type_helper.dart';
void main() async {
asyncStart();
var futures = [for (var i = 0; i < 5; i++) Future<int>.value(i)];
var errors = [for (var i = 0; i < 5; i++) Future<int>.error("e$i")..ignore()];
// Empty list.
Expect.listEquals([], await <Future<int>>[].wait);
// Single future.
Expect.listEquals([0], await <Future<int>>[futures[0]].wait);
// Multiple future.
Expect.listEquals([0, 1, 2, 3, 4], await futures.wait);
// Single error.
try {
await [futures[0], futures[1], errors[2], futures[3], futures[4]].wait;
Expect.fail("Didn't throw");
} on ParallelWaitError<List<int?>, List<AsyncError?>> catch (e) {
Expect.equals(0, e.values[0]);
Expect.equals(1, e.values[1]);
Expect.isNull(e.values[2]);
Expect.equals(3, e.values[3]);
Expect.equals(4, e.values[4]);
Expect.isNull(e.errors[0]);
Expect.isNull(e.errors[1]);
Expect.equals("e2", e.errors[2]?.error);
Expect.isNull(e.errors[3]);
Expect.isNull(e.errors[4]);
}
// Multiple errors.
try {
await [futures[0], errors[1], futures[2], errors[3], futures[4]].wait;
Expect.fail("Didn't throw");
} on ParallelWaitError<List<int?>, List<AsyncError?>> catch (e) {
Expect.equals(0, e.values[0]);
Expect.isNull(e.values[1]);
Expect.equals(2, e.values[2]);
Expect.isNull(e.values[3]);
Expect.equals(4, e.values[4]);
Expect.isNull(e.errors[0]);
Expect.equals("e1", e.errors[1]?.error);
Expect.isNull(e.errors[2]);
Expect.equals("e3", e.errors[3]?.error);
Expect.isNull(e.errors[4]);
}
// Alle errors.
try {
await errors.wait;
Expect.fail("Didn't throw");
} on ParallelWaitError<List<int?>, List<AsyncError?>> catch (e) {
Expect.isNull(e.values[0]);
Expect.isNull(e.values[1]);
Expect.isNull(e.values[2]);
Expect.isNull(e.values[3]);
Expect.isNull(e.values[4]);
Expect.equals("e0", e.errors[0]?.error);
Expect.equals("e1", e.errors[1]?.error);
Expect.equals("e2", e.errors[2]?.error);
Expect.equals("e3", e.errors[3]?.error);
Expect.equals("e4", e.errors[4]?.error);
}
asyncEnd();
}

View file

@ -0,0 +1,441 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:async";
import 'package:async_helper/async_helper.dart';
import "package:expect/expect.dart";
import '../../language/static_type_helper.dart';
void main() async {
asyncStart();
var fi = Future<int>.value(2);
var fb = Future<bool>.value(true);
var fs = Future<String>.value("s");
var fie = Future<int>.error("ie", StackTrace.empty)..ignore();
var fbe = Future<bool>.error("be", StackTrace.empty)..ignore();
var fse = Future<String>.error("se", StackTrace.empty)..ignore();
var fsn = Completer<String>().future; // Never completes.
{
// 2-tuple `wait` getter.
// No error.
var r = await (fi, fb).wait;
Expect.equals((2, true), r);
// Some error.
try {
await (fi, fbe).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<(int?, bool?),
(AsyncError?, AsyncError?)> catch (e, s) {
Expect.equals((2, null), e.values);
Expect.isNull(e.errors.$1);
Expect.equals("be", e.errors.$2?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
// All error.
try {
await (fie, fbe).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<(int?, bool?),
(AsyncError?, AsyncError?)> catch (e, s) {
Expect.equals((null, null), e.values);
Expect.equals("ie", e.errors.$1?.error);
Expect.equals("be", e.errors.$2?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
}
{
// 3-tuple `wait` getter.
// No error.
var r = await (fb, fs, fi).wait;
Expect.equals((true, "s", 2), r);
// Some error.
try {
await (fb, fse, fi).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<(bool?, String?, int?),
(AsyncError?, AsyncError?, AsyncError?)> catch (e, s) {
Expect.equals((true, null, 2), e.values);
Expect.isNull(e.errors.$1);
Expect.equals("se", e.errors.$2?.error);
Expect.isNull(e.errors.$3);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
// All error.
try {
await (fbe, fse, fie).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<(bool?, String?, int?),
(AsyncError?, AsyncError?, AsyncError?)> catch (e, s) {
Expect.equals((null, null, null), e.values);
Expect.equals("be", e.errors.$1?.error);
Expect.equals("se", e.errors.$2?.error);
Expect.equals("ie", e.errors.$3?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
}
{
// 4-tuple `wait` getter.
// No error.
var r = await (fs, fi, fb, fs).wait;
Expect.equals(("s", 2, true, "s"), r);
// Some error.
try {
await (fs, fie, fb, fse).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<(String?, int?, bool?, String?),
(AsyncError?, AsyncError?, AsyncError?, AsyncError?)> catch (e, s) {
Expect.equals(("s", null, true, null), e.values);
Expect.isNull(e.errors.$1);
Expect.equals("ie", e.errors.$2?.error);
Expect.isNull(e.errors.$3);
Expect.equals("se", e.errors.$4?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
// All error.
try {
await (fse, fie, fbe, fse).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<(String?, int?, bool?, String?),
(AsyncError?, AsyncError?, AsyncError?, AsyncError?)> catch (e, s) {
Expect.equals((null, null, null, null), e.values);
Expect.equals("se", e.errors.$1?.error);
Expect.equals("ie", e.errors.$2?.error);
Expect.equals("be", e.errors.$3?.error);
Expect.equals("se", e.errors.$4?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
}
{
// 5-tuple `wait` getter.
// No error.
var r = await (fi, fb, fs, fi, fb).wait;
Expect.equals((2, true, "s", 2, true), r);
// Some error.
try {
await (fi, fbe, fs, fie, fb).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(int?, bool?, String?, int?, bool?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals((2, null, "s", null, true), e.values);
Expect.isNull(e.errors.$1);
Expect.equals("be", e.errors.$2?.error);
Expect.isNull(e.errors.$3);
Expect.equals("ie", e.errors.$4?.error);
Expect.isNull(e.errors.$5);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
// All error.
try {
await (fie, fbe, fse, fie, fbe).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(int?, bool?, String?, int?, bool?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals((null, null, null, null, null), e.values);
Expect.equals("ie", e.errors.$1?.error);
Expect.equals("be", e.errors.$2?.error);
Expect.equals("se", e.errors.$3?.error);
Expect.equals("ie", e.errors.$4?.error);
Expect.equals("be", e.errors.$5?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
}
{
// 6-tuple `wait` getter.
// No error.
var r = await (fb, fs, fi, fb, fs, fi).wait;
Expect.equals((true, "s", 2, true, "s", 2), r);
// Some error.
try {
await (fb, fse, fi, fbe, fs, fie).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(bool?, String?, int?, bool?, String?, int?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals((true, null, 2, null, "s", null), e.values);
Expect.isNull(e.errors.$1);
Expect.equals("se", e.errors.$2?.error);
Expect.isNull(e.errors.$3);
Expect.equals("be", e.errors.$4?.error);
Expect.isNull(e.errors.$5);
Expect.equals("ie", e.errors.$6?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
// All error.
try {
await (fbe, fse, fie, fbe, fse, fie).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(bool?, String?, int?, bool?, String?, int?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals((null, null, null, null, null, null), e.values);
Expect.equals("be", e.errors.$1?.error);
Expect.equals("se", e.errors.$2?.error);
Expect.equals("ie", e.errors.$3?.error);
Expect.equals("be", e.errors.$4?.error);
Expect.equals("se", e.errors.$5?.error);
Expect.equals("ie", e.errors.$6?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
}
{
// 7-tuple `wait` getter.
// No error.
var r = await (fs, fi, fb, fs, fi, fb, fs).wait;
Expect.equals(("s", 2, true, "s", 2, true, "s"), r);
// Some error.
try {
await (fs, fie, fb, fse, fi, fbe, fs).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(String?, int?, bool?, String?, int?, bool?, String?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals(("s", null, true, null, 2, null, "s"), e.values);
Expect.isNull(e.errors.$1);
Expect.equals("ie", e.errors.$2?.error);
Expect.isNull(e.errors.$3);
Expect.equals("se", e.errors.$4?.error);
Expect.isNull(e.errors.$5);
Expect.equals("be", e.errors.$6?.error);
Expect.isNull(e.errors.$7);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
// All error.
try {
await (fse, fie, fbe, fse, fie, fbe, fse).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(String?, int?, bool?, String?, int?, bool?, String?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals((null, null, null, null, null, null, null), e.values);
Expect.equals("se", e.errors.$1?.error);
Expect.equals("ie", e.errors.$2?.error);
Expect.equals("be", e.errors.$3?.error);
Expect.equals("se", e.errors.$4?.error);
Expect.equals("ie", e.errors.$5?.error);
Expect.equals("be", e.errors.$6?.error);
Expect.equals("se", e.errors.$7?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
}
{
// 8-tuple `wait` getter.
// No error.
var r = await (fi, fb, fs, fi, fb, fs, fi, fb).wait;
Expect.equals((2, true, "s", 2, true, "s", 2, true), r);
// Some error.
try {
await (fi, fbe, fs, fie, fb, fse, fi, fbe).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(int?, bool?, String?, int?, bool?, String?, int?, bool?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals((2, null, "s", null, true, null, 2, null), e.values);
Expect.isNull(e.errors.$1);
Expect.equals("be", e.errors.$2?.error);
Expect.isNull(e.errors.$3);
Expect.equals("ie", e.errors.$4?.error);
Expect.isNull(e.errors.$5);
Expect.equals("se", e.errors.$6?.error);
Expect.isNull(e.errors.$7);
Expect.equals("be", e.errors.$8?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
// All error.
try {
await (fie, fbe, fse, fie, fbe, fse, fie, fbe).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(int?, bool?, String?, int?, bool?, String?, int?, bool?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals((null, null, null, null, null, null, null, null), e.values);
Expect.equals("ie", e.errors.$1?.error);
Expect.equals("be", e.errors.$2?.error);
Expect.equals("se", e.errors.$3?.error);
Expect.equals("ie", e.errors.$4?.error);
Expect.equals("be", e.errors.$5?.error);
Expect.equals("se", e.errors.$6?.error);
Expect.equals("ie", e.errors.$7?.error);
Expect.equals("be", e.errors.$8?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
}
{
// 9-tuple `wait` getter.
// No error.
var r = await (fb, fs, fi, fb, fs, fi, fb, fs, fi).wait;
Expect.equals((true, "s", 2, true, "s", 2, true, "s", 2), r);
// Some error.
try {
await (fb, fse, fi, fbe, fs, fie, fb, fse, fi).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(bool?, String?, int?, bool?, String?, int?, bool?, String?, int?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals((true, null, 2, null, "s", null, true, null, 2), e.values);
Expect.isNull(e.errors.$1);
Expect.equals("se", e.errors.$2?.error);
Expect.isNull(e.errors.$3);
Expect.equals("be", e.errors.$4?.error);
Expect.isNull(e.errors.$5);
Expect.equals("ie", e.errors.$6?.error);
Expect.isNull(e.errors.$7);
Expect.equals("se", e.errors.$8?.error);
Expect.isNull(e.errors.$9);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
// All error.
try {
await (fbe, fse, fie, fbe, fse, fie, fbe, fse, fie).wait;
Expect.fail("Did not throw");
} on ParallelWaitError<
(bool?, String?, int?, bool?, String?, int?, bool?, String?, int?),
(
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?,
AsyncError?
)> catch (e, s) {
Expect.equals(
(null, null, null, null, null, null, null, null, null), e.values);
Expect.equals("be", e.errors.$1?.error);
Expect.equals("se", e.errors.$2?.error);
Expect.equals("ie", e.errors.$3?.error);
Expect.equals("be", e.errors.$4?.error);
Expect.equals("se", e.errors.$5?.error);
Expect.equals("ie", e.errors.$6?.error);
Expect.equals("be", e.errors.$7?.error);
Expect.equals("se", e.errors.$8?.error);
Expect.equals("ie", e.errors.$9?.error);
} on Object catch (e) {
Expect.fail("Did not throw expected error: ${e.runtimeType}");
}
}
asyncEnd();
}