wait<T> 静态方法
等待多个 Future 完成,并收集它们的结果。
返回一个 Future,一旦所有提供的 Future 完成后,它将完成,无论是通过结果还是通过错误(如果提供的任何一个 Future 失败)。
返回 Future 的值将是一个列表,其中包含按照提供的 Future 的顺序迭代 futures
产生的所有值。
如果任何一个 Future 以错误完成,则返回的 Future 也以那个错误完成。如果后续的 Future 也以错误完成,则这些错误将被丢弃。
如果 eagerError
为 true,则一旦其中一个 Future 出错,返回的 Future 立即以错误完成。否则,必须在返回的 Future 完成之前,所有 Future 都必须完成(仍然是以第一个错误;剩余的错误将被静默丢弃)。
如果在错误的情况下,提供了 cleanUp
,则会在任何成功的 Future 的非空结果上调用 cleanUp
。这使得可以清理那些否则会丢失的资源(因为返回的 Future 不提供对这些值的访问)。如果没有错误,cleanUp
函数不会被使用。
调用 cleanUp
不应该抛出异常。如果它抛出了异常,那么它将是一个未捕获的异步错误。
示例
void main() async {
var value = await Future.wait([delayedNumber(), delayedString()]);
print(value); // [2, result]
}
Future<int> delayedNumber() async {
await Future.delayed(const Duration(seconds: 2));
return 2;
}
Future<String> delayedString() async {
await Future.delayed(const Duration(seconds: 2));
return 'result';
}
实现
static Future<List<T>> wait<T>(Iterable<Future<T>> futures,
{bool eagerError = false, void cleanUp(T successValue)?}) {
@pragma('vm:awaiter-link')
final _Future<List<T>> _future = _Future<List<T>>();
List<T?>? values; // Collects the values. Set to null on error.
int remaining = 0; // How many futures are we waiting for.
Object? error; // The first error from a future.
StackTrace? stackTrace; // The stackTrace that came with the error.
// Handle an error from any of the futures.
void handleError(Object theError, StackTrace theStackTrace) {
var remainingResults = --remaining;
List<T?>? valueList = values;
if (valueList != null) {
// First error, set state to represent error having already happened.
values = null;
error = theError;
stackTrace = theStackTrace;
// Then clean up any already successfully produced results.
if (cleanUp != null) {
for (var value in valueList) {
if (value != null) {
// Ensure errors from `cleanUp` are uncaught.
T cleanUpValue = value;
Future.sync(() {
cleanUp(cleanUpValue);
});
}
}
}
if (remainingResults == 0 || eagerError) {
_future._completeError(theError, theStackTrace);
}
} else {
// Not the first error.
if (remainingResults == 0 && !eagerError) {
// Last future completed, non-eagerly report the first error.
_future._completeError(error!, stackTrace!);
}
}
}
try {
// As each future completes, put its value into the corresponding
// position in the list of values.
for (var future in futures) {
int pos = remaining;
future.then((T value) {
var remainingResults = --remaining;
List<T?>? valueList = values;
if (valueList != null) {
// No errors yet.
assert(valueList[pos] == null);
valueList[pos] = value;
if (remainingResults == 0) {
_future._completeWithValue(
[for (var value in valueList) value as T]);
}
} else {
// Prior error, clean-up this value if necessary.
if (cleanUp != null && value != null) {
// Ensure errors from cleanUp are uncaught.
Future.sync(() {
cleanUp(value);
});
}
if (remainingResults == 0 && !eagerError) {
// Last future completed, non-eagerly report the first error.
_future._completeError(error!, stackTrace!);
}
}
}, onError: handleError);
// Increment the 'remaining' after the call to 'then'.
// If that call throws, we don't expect any future callback from
// the future, and we also don't increment remaining.
remaining++;
}
if (remaining == 0) {
// No elements in iterable.
return _future.._completeWithValue(<T>[]);
}
values = List<T?>.filled(remaining, null);
} catch (e, st) {
// The error must have been thrown while iterating over the futures
// list, or while installing a callback handler on the future.
// This is a breach of the `Future` protocol, but we try to handle it
// gracefully.
if (remaining == 0 || eagerError) {
// Throw a new Future.error.
// Don't just call `_future._completeError` since that would propagate
// the error too eagerly, not giving the callers time to install
// error handlers.
// Also, don't use `_asyncCompleteError` since that one doesn't give
// zones the chance to intercept the error.
return new Future.error(e, st);
} else {
// Don't allocate a list for values, thus indicating that there was an
// error.
// Set error to the caught exception.
error = e;
stackTrace = st;
}
}
return _future;
}