How to use async code inside map() (Flutter, Firestore)

*爱你&永不变心* 提交于 2020-12-06 16:08:23

问题


I am working on a group chat app with Flutter and the Firestore Plugin. Getting the data from the database and converting the snapshot into a List of Messages works totally fine. But now I want to convert the uid from the database into the username (the uids and their usernames are saved in the db). This is my code:

final CollectionReference messagesCollection =
    Firestore.instance.collection('messages');

final CollectionReference usersCollection =
    Firestore.instance.collection('users');

Future<String> getUsernameByUID(String _uid) async {
  String username =
      await usersCollection.document(uid).get().then((querySnapshot) {
    return (querySnapshot.data["username"]);
  });
  return username;
}

List<Message> _messagesFromSnapshot(QuerySnapshot snapshot){
  return snapshot.documents.map((doc) {

    String username = await getUsernameByUID(doc.data["uid"]);

    return Message(
      text: doc.data["text"] ?? "",
      username: username ?? "",
      time: doc.data["time"] ?? "",
    );
  }).toList();
}


Stream<List<Message>> get messages {
  return messagesCollection
      .orderBy("time")
      .snapshots()
      .map(_messagesFromSnapshot);
}

The problem is in this line, because I cannot run this async code inside of the map().

String username = await getUsernameByUID(doc.data["uid"]);

Is there a solution to fix this? Thanks in advance.


回答1:


async functions must return a Future, so adding async keyword to your callback means that your List.map() call must now return a List of Futures.

You can convert a List<Future<Message>> to a List<Message> by using Future.wait:

Future<List<Message>> _messagesFromSnapshot(QuerySnapshot snapshot) async {
  var futures = snapshot.documents.map((doc) async {
    String username = await getUsernameByUID(doc.data["uid"]);

    return Message(
      text: doc.data["text"] ?? "",
      username: username ?? "",
      time: doc.data["time"] ?? "",
    );
  });

  return await Future.wait(futures);
}

Of course, Future.wait returns a Future and must be awaited, so now _messagesFromSnapshot must be async as well. Asynchrony is contagious, and that then would affect any callers of _messagesFromSnapshot.

Since the messages getter returns a Stream and is already asynchronous, I believe that you instead can use Stream.asyncMap:

Stream<List<Message>> get messages {
  return messagesCollection
      .orderBy("time")
      .snapshots()
      .asyncMap(_messagesFromSnapshot);
}



回答2:


What you need is Future.forEach method. it iterates over a list and waits until all the async methods to finish before moving on to the next item in the list.

Future<List<Message>> _messagesFromSnapshot(QuerySnapshot snapshot) async {
  List<Message> _messages = [];
  await Future.forEach(snapshot.documents, (doc) async {
    String username = await getUsernameByUID(doc.data["uid"]);
    _messages.add(
       Message(
        text: doc.data["text"] ?? "",
        username: username ?? "",
        time: doc.data["time"] ?? "",)
     );   
    });   
  return _messages; 
}

Here is an example dartpad



来源:https://stackoverflow.com/questions/58978730/how-to-use-async-code-inside-map-flutter-firestore

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!