import 'dart:async'; import 'dart:collection'; Future main() async { final list = await [ Future.delayed(const Duration(milliseconds: 1500), () => 1), Future.delayed(const Duration(milliseconds: 5000), () => throw '2'), Future.delayed(const Duration(milliseconds: 1200), () => 3), Future.delayed(const Duration(milliseconds: 8000), () => throw '4'), Future.delayed(const Duration(milliseconds: 4300), () => 5), Future.delayed(const Duration(milliseconds: 3000), () => 6), Future.delayed(const Duration(milliseconds: 7000), () => 6), Future.delayed(const Duration(milliseconds: 3000), () => 7), Future.delayed(const Duration(milliseconds: 4000), () => 8), Future.delayed(const Duration(milliseconds: 3000), () => 9), Future.delayed(const Duration(milliseconds: 6000), () => 10), ].allSettled( parallel: 2, onSuccess: (index, value) { print('index: $index, value: $value'); }, onError: (index, e, s) { print('index: $index, error: $e'); }, ); print(list); } extension IterableFuture on Iterable> { Future>> allSettled({ int parallel = 0, void Function(int index, T value)? onSuccess, void Function(int index, Object error, StackTrace stackTrace)? onError, void Function( int index, FutureResultStatus status, T? value, Object? error, StackTrace? stackTrace, )? onComplete, }) { final len = length; if (len == 0) { return Future.value(>[]); } final completer = Completer>>(); int remaining = len; final list = List?>.filled(len, null); if (parallel <= 0) { void handleFuture(int index, Future future) { future.then((value) { list[index] = FutureResult.success(value); onSuccess?.call(index, value); onComplete?.call( index, FutureResultStatus.success, value, null, null); }).catchError((e, s) { list[index] = FutureResult.error(e, s); onError?.call(index, e, s); onComplete?.call(index, FutureResultStatus.error, null, e, s); }).whenComplete(() { if (--remaining == 0) { completer.complete(List.unmodifiable(list.cast())); } }); } for (int i = 0; i < len; i++) { handleFuture(i, elementAt(i)); } } else { int running = 0; int index = 0; final queue = Queue>.from(this); void exec() { void handleFuture(int index, Future future) { future.then((value) { list[index] = FutureResult.success(value); onSuccess?.call(index, value); onComplete?.call( index, FutureResultStatus.success, value, null, null, ); }).catchError((e, s) { list[index] = FutureResult.error(e, s); onError?.call(index, e, s); onComplete?.call(index, FutureResultStatus.error, null, e, s); }).whenComplete(() { if (--remaining == 0) { completer.complete(List.unmodifiable(list.cast())); } running--; exec(); }); } while (queue.isNotEmpty && running < parallel) { running++; final future = queue.removeFirst(); handleFuture(index++, future); } } exec(); } return completer.future; } } enum FutureResultStatus { success, error, ; } class FutureResult { factory FutureResult.success(T value) { return FutureResult._success(FutureResultStatus.success, value); } factory FutureResult.error(Object error, StackTrace stackTrace) { return FutureResult._error(FutureResultStatus.error, error, stackTrace); } FutureResult._success(this.status, this._value); FutureResult._error(this.status, this._error, this._stackTrace); final FutureResultStatus status; late final T _value; late final Object _error; late final StackTrace _stackTrace; FutureOr when({ FutureOr Function(T value)? success, FutureOr Function(Object error, StackTrace stackTrace)? error, }) { switch (status) { case FutureResultStatus.success: return success?.call(_value); case FutureResultStatus.error: return error?.call(_error, _stackTrace); } } @override String toString() { return 'FutureResult(status: $status, ${when(success: (value) => 'value: $_value', error: (e, s) => 'error: $_error, stackTrace: $_stackTrace')})'; } }