Wait for all the given futures to complete and collect their values.
Returns a future which will complete once all the futures in a list are complete. If any of the futures in the list completes with an error, the resulting future also completes with an error. Otherwise the value of the returned future will be a list of all the values that were produced.
If eagerError
is true, the future completes with an error immediately on
the first error from one of the futures. Otherwise all futures must
complete before the returned future is completed (still with the first
error to occur, the remaining errors are silently dropped).
If cleanUp
is provided, in the case of an error, any non-null result of
a successful future is passed to cleanUp
, which can then release any
resources that the successful operation allocated.
The call to cleanUp
should not throw. If it does, the error will be an
uncaught asynchronous error.
Source
static Future<List/*<T>*/> wait/*<T>*/(Iterable<Future/*<T>*/> futures,
{bool eagerError: false,
void cleanUp(/*=T*/ successValue)}) {
final _Future<List/*<T>*/> result = new _Future<List/*<T>*/>();
List/*<T>*/ values; // Collects the values. Set to null on error.
int remaining = 0; // How many futures are we waiting for.
var 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(theError, theStackTrace) {
remaining--;
if (values != null) {
if (cleanUp != null) {
for (var value in values) {
if (value != null) {
// Ensure errors from cleanUp are uncaught.
new Future.sync(() { cleanUp(value); });
}
}
}
values = null;
if (remaining == 0 || eagerError) {
result._completeError(theError, theStackTrace);
} else {
error = theError;
stackTrace = theStackTrace;
}
} else if (remaining == 0 && !eagerError) {
result._completeError(error, stackTrace);
}
}
// As each future completes, put its value into the corresponding
// position in the list of values.
for (Future future in futures) {
int pos = remaining++;
future.then((Object/*=T*/ value) {
remaining--;
if (values != null) {
values[pos] = value;
if (remaining == 0) {
result._completeWithValue(values);
}
} else {
if (cleanUp != null && value != null) {
// Ensure errors from cleanUp are uncaught.
new Future.sync(() { cleanUp(value); });
}
if (remaining == 0 && !eagerError) {
result._completeError(error, stackTrace);
}
}
}, onError: handleError);
}
if (remaining == 0) {
return new Future.value(const []);
}
values = new List/*<T>*/(remaining);
return result;
}