wait<T> 静态方法
等待多个Future完成并收集其结果。
返回一个Future,它将在所有提供的Future都完成后完成,无论是通过其结果还是通过任何提供的Future失败时出现的错误。
返回Future的值将是一个列表,包含通过迭代 futures
产生的所有值,顺序与提供的Future顺序相同。
如果有任何Future以错误完成,则返回的Future将以该错误完成。如果其他Future也以错误完成,则忽略这些错误。
如果 eagerError
为true,则返回的Future在第一个Future发生错误时立即以错误完成。否则,所有Future必须完成,返回的Future才会完成(仍然以第一个错误完成;剩余的错误被静默丢弃)。
在出现错误的情况下,如果提供了 cleanUp
,则会在任何非null的成功Future结果上调用 cleanUp
。这使得能够 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;
}