How to apply an async
function with .map()
to each element of a list.
Thing you want to do
As a simple example, consider code that converts a Firestore DocumentSnapshot
into a model
class.
List<Model> models = docs.map((doc) => _fromDoc(doc)).toList();
I think it's common to prepare a method called _fromDoc
like this, apply _fromDoc
to each DocumentSnapshot
with .map()
on the list of DocumentSnapshots
, and convert it to a list of model classes.
Now suppose the _fromDoc
method was an async
function.
For example:
List<Model> models = docs.map((doc) async => await _fromDoc(doc)).toList();
Don't you think it might work if you do it like this? Unfortunately this doesn't work 😓
If you think about it, you can see that
(doc) async => await _fromDoc(doc)
Since this is itself an async
function, the return value is a Future<Model>
instead of a Model
.
That's why,
docs.map((doc) async => await _fromDoc(doc)).toList()
The type of this whole thing will be List<Future<Model>>
instead of List<Model>
.
in short,
docs.map((doc) => _fromDoc(doc)).toList()
After all, it's the same as the original.
How to ... ?
So what to do is use Future.wait()
.
Future.wait()
converts Iterable<Future<T>>
to Future<List<T>>
, so if you await this, you can safely get the list of asynchronously resolved values.
List<Model> models = await Future.wait(docs.map((doc) => _fromDoc(doc)).toList());
Now you have applied the async
function with .map()
to each element of the list as intended 👍
There are comments.