Dart Future.wait for multiple futures and get back results of different types
Solution 1
You need to adapt each of your Future<T>
s to a common type of Future
. You could use Future<void>
:
List<Foo> foos;
List<Bar> bars;
List<FooBars> foobars;
await Future.wait<void>([
downloader.getFoos().then((result) => foos = result),
downloader.getBars().then((result) => bars = result),
downloader.getFooBars().then((result) => foobars = result),
]);
processData(foos, bars, foobars);
Or if you prefer await
to .then()
, the Future.wait
call could be:
await Future.wait<void>([
(() async => foos = await downloader.getFoos())(),
(() async => bars = await downloader.getBars())(),
(() async => foobars = await downloader.getFooBars())(),
]);
Solution 2
I'd say the correct answer is to use the official async package and FutureGroup
:
void main() {
Future<String> future1 = getData(2);
Future<String> future2 = getData(4);
Future<String> future3 = getData(6);
FutureGroup futureGroup = FutureGroup();
futureGroup.add(future1);
futureGroup.add(future2);
futureGroup.add(future3);
futureGroup.close();
futureGroup.future.then((value) => {print(value)});
}
Future<String> getData(int duration) async {
await Future.delayed(Duration(seconds: duration)); //Mock delay
return "This a test data";
}
Solution 3
I think is not possible to do in a super nice fashion. All you can do is something like this:
void main() async {
List<List<dynamic>> result = await Future.wait<List<dynamic>>([
getStringData(),
getIntData(),
]);
print(result[0]);
print(result[1]);
}
Future<List<String>> getStringData() {
return Future.value(["a", "b"]);
}
Future<List<int>> getIntData() {
return Future.value([1, 2]);
}
Solution 4
I made a helper function that utilizes some of the logic in the other answers. It uses the tuple
package, but you can write it yourself pretty easily (included below).
// Put this in future_utils.dart
/// Represents a 2-tuple, or pair.
class Tuple2<T1, T2> {
/// Returns the first item of the tuple
final T1 item1;
/// Returns the second item of the tuple
final T2 item2;
/// Creates a new tuple value with the specified items.
const Tuple2(this.item1, this.item2);
}
Future<Tuple2<T1, T2>> await2<T1, T2>(
Future<T1> Function() firstFuture,
Future<T2> Function() secondFuture) async {
late T1 item1;
late T2 item2;
await Future.wait<void>([
(() async => item1 = await firstFuture())(),
(() async => item2 = await secondFuture())(),
]);
return Tuple2(item1, item2);
}
Then call it:
Future<List<Foo>> foos = downloader.getFoos;
Future<List<Bar>> bars = downloader.getBars;
// Will run in parallel
Tuple2<List<Foo>, List<Bar>> results = await await2(foos, bars);
// Do stuff with the results since both are complete
print(results.item1[0]);
print(results.item2[0]);
Now if you want one for 3 arguments, 4, or more you can just copy and paste (await3
, await4
). This isn't too crazy of a pattern, I've used it for multiple lets in Kotlin and also that Tuple library I linked.
James Allen
Updated on December 19, 2022Comments
-
James Allen over 1 year
I'm using Flutter to download 3 different sets of data from a server, then do something with all 3 sets. I could do this:
List<Foo> foos = await downloader.getFoos(); List<Bar> bars = await downloader.getBars(); List<FooBars> foobars = await downloader.getFooBars(); processData(foos, bars, foobars);
But I'd prefer to download all 3 data sets asynchronously in parallel. I've seen that Dart has this Future.wait method:
Future<List<T>> wait <T>( Iterable<Future<T>> futures, { bool eagerError: false, void cleanUp( T successValue ) })
However it looks like this will only return values of the same type (T). I have 3 different types, so I don't see how I can use this and get my 3 data sets back.
What's the best alternative way to achieve this?
Thanks!