description

# wait<T> static method

Future<List<T>> wait<T>(

1. Iterable<Future<T>> futures, {
2. bool eagerError = false,
3. void cleanUp(
   1. T successValue
   )?,

})

Waits for multiple futures to complete and collects their results.

Returns a future which will complete once all the provided futures have completed, either with their results, or with an error if any of the provided futures fail.

The value of the returned future will be a list of all the values that were produced in the order that the futures are provided by iterating `futures`.

If any future completes with an error, then the returned future completes with that error. If further futures also complete with errors, those errors are discarded.

If `eagerError` is true, the returned 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; the remaining errors are silently dropped).

In the case of an error, `cleanUp` (if provided), is invoked on any non-null result of successful futures. This makes it possible to `cleanUp` resources that would otherwise be lost (since the returned future does not provide access to these values). The `cleanUp` function is unused if there is no error.

The call to `cleanUp` should not throw. If it does, the error will be an uncaught asynchronous error.

Example:

    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';
    }

## Implementation

    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, s) {
        // 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) {
          // Complete asynchronously to give receiver time to handle
          // the result.
          return _future.._asyncCompleteErrorObject(_interceptCaughtError(e, s));
        } else {
          // Don't allocate a list for values, thus indicating that there was an
          // error.
          // Set error to the caught exception.
          error = e;
          stackTrace = s;
        }
      }
      return _future;
    }