I have a Map<String, List<Future<Foo>>>
that I want to convert to a Map<String, List<Foo>>
(wait for futures to finish). Currently, this is how I perform the operation:
// source contains values with an async operation; convert to a map
final futures = source.map(
(k, v) => MapEntry(k, v.map((e) async => await e.asyncOperation())),
);
final json = {};
// works, but must wait for each entry to process
for (final kvp in futures.entries) {
json[kvp.key] = await Future.wait(kvp.value);
}
This will block for every entry sublist, is there a way to generate the proper Map<String, List<Foo>>
while at the same time awaiting all of the inner list futures?
I can do await Future.wait(futures.values.flattened);
, but how would I reassign the results back to the proper map key?
One reason to complete all the futures immediately would be that they already exist, and if you don't await all of them immediately, one of them might complete with an (unhandled!) error, which is bad.
The standard provided way to wait for more than one future at a time is Future.wait
, which takes an Iterable<Future<X>>
and returns a Future<List<X>>
.
That will directly help you with the individual lists, but then you'll have a Future<List<Foo>>
per key in the map. You'll have to convert those to a list too.
So, maybe something like:
Future<Map<String, List<Foo>>> waitAll(
Map<String, Iterable<Future<Foo>>> map) async =>
Map.fromIterables(map.keys.toList(), await Future.wait(map.values.map(Future.wait));
UPDATE: Since Dart 3, there is now a .wait
extension getter which works on iterables and records (up to nine positional fields). The record versions
allows you to await futures with different types without losing the type
information. It also has better error handling.
So await Future.wait(...)
can be written await (...).wait
instead.