【问题标题】:How to use async code inside map() (Flutter, Firestore)如何在 map() 中使用异步代码(Flutter、Firestore)
【发布时间】:2020-03-17 14:38:51
【问题描述】:

我正在使用 Flutter 和 Firestore 插件开发一个群聊应用程序。从数据库中获取数据并将快照转换为消息列表完全可以正常工作。但现在我想将数据库中的 uid 转换为用户名(uid 及其用户名保存在数据库中)。这是我的代码:

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);
}

问题出在这一行,因为我无法在 map() 中运行此异步代码。

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

有解决办法吗?提前致谢。

【问题讨论】:

    标签: firebase flutter dart google-cloud-firestore


    【解决方案1】:

    你需要的是Future.forEach 方法。它遍历一个列表并等待所有异步方法完成,然后再移动到列表中的下一个项目。

    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; 
    }
    

    这是一个例子dartpad

    【讨论】:

    • 因为_messagesFromSnapshotasync,它现在必须返回一个Future&lt;List&lt;Message&gt;&gt;,并且_messagesFromSnapshot 的调用者必须相应地更新(以及它们的调用者,等等)。
    • 你无法逃避
    • 我没有声称你可以逃脱它。我的观点是,您的答案不完整,因为它没有提到_messagesFromSnapshot 的调用者需要进行哪些更改。
    【解决方案2】:

    async 函数必须返回 Future,因此在回调中添加 async 关键字意味着您的 List.map() 调用现在必须返回 ListFutures。

    您可以使用Future.waitList&lt;Future&lt;Message&gt;&gt; 转换为List&lt;Message&gt;

    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);
    }
    

    当然,Future.wait 返回一个Future 并且必须是awaited,所以现在_messagesFromSnapshot 也必须是async。异步具有传染性,然后会影响_messagesFromSnapshot 的任何调用者。

    由于messages getter 返回一个Stream 并且已经是异步的,我相信您可以改用Stream.asyncMap

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

    【讨论】:

    • 谢谢你,正是我想要的!
    猜你喜欢
    • 1970-01-01
    • 2021-01-13
    • 2019-05-29
    • 1970-01-01
    • 2021-09-10
    • 2021-03-22
    • 2022-01-19
    • 2019-12-18
    • 1970-01-01
    相关资源
    最近更新 更多