In simple terms, Future.wait() allows you to run multiple Futures in parallel and wait until all of them are completed before proceeding. Itβs part of the dart:async library and is very useful when you have several independent async tasks that can run at the same time β for example, making multiple API calls or loading several files concurrently.
Hereβs the basic syntax:
Future.wait();
It returns a single Future that completes when all the Futures in the list have completed.
For example, letβs say I want to fetch data from three different APIs at once:
Future main() async {
final results = await Future.wait([
fetchUserData(),
fetchOrders(),
fetchNotifications(),
]);
print(results); // Prints a list of all results
}
Future fetchUserData() async {
await Future.delayed(Duration(seconds: 2));
return 'User data loaded';
}
Future fetchOrders() async {
await Future.delayed(Duration(seconds: 3));
return 'Orders loaded';
}
Future fetchNotifications() async {
await Future.delayed(Duration(seconds: 1));
return 'Notifications loaded';
}
In this example, all three async functions run simultaneously.
Even though the total delay times add up to 6 seconds, the program completes in just around 3 seconds (the time of the longest Future).
When the Future.wait() completes, it gives me a List of results, preserving the same order as the input list β meaning result [0] corresponds to fetchUserData(), [1] to fetchOrders(), and so on.
Now, one key point β error handling.
By default, if any of the Futures in the list throw an error, Future.wait() will complete with that error immediately and discard the results of the remaining Futures.
For example:
Future.wait([
fetchData1(),
fetchDataWithError(),
fetchData2(),
]);
If fetchDataWithError() throws an exception, Future.wait() completes with that error, and the other results are ignored.
However, you can override that behavior using the eagerError and cleanUp parameters:
Future.wait(
futures,
eagerError: true,
cleanUp: (successValue) {
// Optional clean-up logic for successfully completed futures
},
);
eagerError: true means it stops immediately on the first error (default behavior).
eagerError: false means it waits for all Futures to finish, then throws an error if any failed.
I used Future.wait() extensively in a Flutter app where I had to load a userβs profile, recent transactions, and preferences simultaneously before displaying the dashboard. Instead of awaiting each one sequentially, I combined them:
dart
Copy code
final results = await Future.wait([
api.getUserProfile(),
api.getRecentTransactions(),
api.getUserPreferences(),
]);
This reduced the load time from nearly 5 seconds (sequential) to just around 2 seconds (parallel).
A challenge Iβve faced with Future.wait() is partial failure handling β if one Future fails but others succeed, I sometimes need those partial results. In that case, instead of failing the entire Future.wait(), I handle errors individually:
Future.wait([
fetchA().catchError((e) => 'Error A: $e'),
fetchB().catchError((e) => 'Error B: $e'),
]);
This way, the entire batch completes successfully, and I can still inspect individual results or errors.
A limitation of Future.wait() is that itβs not suitable for scenarios where tasks depend on each other (i.e., the result of one Future is needed before the next starts). In that case, I prefer sequential awaits or chaining Futures with .then().
As an alternative, when working with continuous or event-based async data, I use Streams instead of Future.wait() β since Future.wait() only handles one-time operations, not ongoing events.
In summary, I use Future.wait() when I need to:
- Execute multiple independent async operations in parallel
- Wait until all of them finish before proceeding
- Aggregate all results efficiently
And I handle it carefully by:
- Managing errors per Future when partial results are needed
- Avoiding it when task order or dependency matters
- Using Streams for continuous data instead
In practice, Future.wait() is one of my go-to tools for optimizing async workflows β it helps cut down execution time significantly while keeping async code clean and maintainable.
