簡體   English   中英

為什么“return”不等待 Flutter/Dart 中嵌套的“then”?

[英]Why is 'return' not awaiting this nested 'then' in Flutter/Dart?

我是編程新手,這是我關於堆棧溢出的第一篇文章,在構建我的第一個 Flutter 應用程序時,我試圖理解代碼。 而且我不確定為什么兩段代碼的行為不同。 隨意查看代碼並回答為什么一個不起作用..,或者如果你願意。 這是背景。

數據結構:

Collection: chatters Document: chatter doc SubCollection: members-chatter, another SubCollection: approved-chatter 文檔:member doc, and approved doc

我列出了用戶所屬的所有聊天文檔,因此從成員文檔中帶有 uid 的 CollectionGroup 查詢中,我然后查找父文檔 ID。 接下來,我想將聊天文檔標記為 bool public,並標記為公共聊天。 如果用戶的 uid 也在 SubCol approved-chatter 中的批准文檔中,我只希望列出它們。

所以我的主要問題是為什么 await 在我的第一次嘗試中沒有貫穿整個嵌套的.then

但是,當我在這里時,我也願意接受任何關於我處理公共和需要批准類型的團體成員資格的方法的見解。 考慮到讀/寫權限和適當的安全性和隱私,這似乎比我最初想象的要棘手。

我先試過這個。

// get my chatter IDs
Future<List<String>> oldgetMyChatterIDs() async {
  List<String> myChatterIDs = [];

  await FirebaseFirestore.instance
      .collectionGroup('members-chatter')
      .where('uid', isEqualTo: FirebaseAuth.instance.currentUser?.uid)
      .where('status', isEqualTo: 'joined')
      .orderBy('timeLastActive', descending: true)
      .get()
      .then(
        (snapshot) => snapshot.docs.forEach((document) {
           document.reference.parent.parent!.get().then((value) { 
           // the above 'then' isn't awaited. 
            if (value.data()?['public'] ?? true) {
              myChatterIDs.add(document.reference.parent.parent!.id);
              // 'myChatterIDs' is returned empty before the above line fills the list.
            } else {
              // check if user is approved.
            }
          });
        }),
      );

  //  // adding the below forced delay makes the code work... but why aren't the 'thens' above working to holdup the return?
  // await Future.delayed(const Duration(seconds: 1));

  return myChatterIDs;
}

return myChatterIDs; 完成之前:

document.reference.parent.parent!.get().then((value) {
            if (value.data()?['public'] ?? true) {
              myChatterIDs.add(document.reference.parent.parent!.id);
            }

為什么不返回等待等待?

我重寫了代碼,這種方式有效,但我不確定為什么會有所不同。 它確實看起來更容易理解,所以我也許這樣更好。

// get my chatter IDs
Future<List<String>> getMyChatterIDs() async {
  List<String> myChatterIDs = [];

   QuerySnapshot<Map<String, dynamic>> joinedChattersSnapshot = await FirebaseFirestore
      .instance
      .collectionGroup('members-chatter')
      .where('uid', isEqualTo: FirebaseAuth.instance.currentUser?.uid)
      .where('status', isEqualTo: 'joined')
      .orderBy('timeLastActive', descending: true)
      .get();

  for (var i = 0; i < joinedChattersSnapshot.docs.length; i++) {
    DocumentSnapshot<Map<String, dynamic>> aChatDoc =
        await joinedChattersSnapshot.docs[i].reference.parent.parent!.get();
    bool isPublic = aChatDoc.data()?['public'] ?? true;
    if (isPublic) {
      myChatterIDs.add(aChatDoc.id);
    } else {
      try {
        DocumentSnapshot<Map<String, dynamic>> anApprovalDoc =
            await FirebaseFirestore.instance
                .collection('chatters')
                .doc(aChatDoc.id)
                .collection('approved-chatter')
                .doc(FirebaseAuth.instance.currentUser!.uid)
                .get();
        bool isApproved = anApprovalDoc.data()!['approved'];
        if (isApproved) {
          myChatterIDs.add(aChatDoc.id);
        }
      } catch (e) {
        // // Could add pending to another list such as
        // myPendingChatterIDs.add(aChatDoc.id);
      }
    }
  }

  return myChatterIDs;
}

看一下這段代碼:

print("1");
print("2");

結果:

1
2

兩行的打印是同步的,他們不等待任何東西,所以他們會一個接一個地立即執行,對吧?

現在看看這個:

print("1");
await Future.delayed(Duration(seconds: 2));
print("2");

結果:

1
2

現在將打印 1,然后等待 2 秒,然后在調試控制台上打印 2,在這種情況下使用await將停止該行上的代碼,直到它完成(直到 2 秒過去)。

現在看看這段代碼:

print("1");
Future.delayed(Duration(seconds: 2)).then((_) {print("2");});
print("3");

結果:

1
3
2

這似乎是在使用Future ,但它不會等待,因為我們設置的代碼是同步的,所以會打印 1 ,它會轉到下一行,它會運行Future.delayed但它不會等待它跨越全局代碼,所以它會轉到下一行並立即打印 3,當前面的Future.delayed完成時,它會運行then塊內的代碼,所以它會在最后打印 2。

在你的代碼中

await FirebaseFirestore.instance
      .collectionGroup('members-chatter')
      .where('uid', isEqualTo: FirebaseAuth.instance.currentUser?.uid)
      .where('status', isEqualTo: 'joined')
      .orderBy('timeLastActive', descending: true)
      .get()
      .then(
        (snapshot) => snapshot.docs.forEach((document) {
           document.reference.parent.parent!.get().then((value) { 
           // the above 'then' isn't awaited. 
            if (value.data()?['public'] ?? true) {
              myChatterIDs.add(document.reference.parent.parent!.id);
              // 'myChatterIDs' is returned empty before the above line fills the list.
            } else {
              // check if user is approved.
            }
          });
        }),
      );

  //  // adding the below forced delay makes the code work... but why aren't the 'thens' above working to holdup the return?
  // await Future.delayed(const Duration(seconds: 1));

  return myChatterIDs;

使用thenreturn myChatterIDs將在它之前的Future之后立即執行,這將導致函數立即結束。 要使用then修復此問題,您只需將return myChatterIDs移動到then代碼塊中,如下所示:

/*...*/.then(() {
        // ...
  return myChatterIDs
});

在第二個示例中使用await關鍵字將暫停該行的代碼,直到Future完成,然后它會繼續其他人。

通過在 IDE 的方法行上設置斷點,您可以實時查看代碼中發生的情況,然后查看先運行的內容和晚運行的內容。

這是因為document.reference.parent.parent..get()也是一個 Future 但你並沒有告訴你的函數等待它。 第一個 await 只適用於第一個 Future。

當你使用then時,你基本上是在說,執行這個 Future,當它完成時,執行這個函數內部的操作,但該函數之外的所有內容都不會等待then完成,除非你用await告訴它。

例如這段代碼:

String example = 'Number';

await Future.delayed(const Duration(seconds: 1)).then(
  (value) {
    //Here it doesn't have an await so it will execute
    //asynchronously and just continues to next line
    //while the Future executes in the background. 
    Future.delayed(const Duration(seconds: 1)).then(
      (value) {
        example = 'Letter';
      },
    );
  },
);

print(example);

將導致在 1 秒后打印Number 但是這段代碼:

String example = 'Number';

await Future.delayed(const Duration(seconds: 1)).then(
  (value) async {
    //Here it does have an await so it will wait for this
    //future to complete too.
    await Future.delayed(const Duration(seconds: 1)).then(
      (value) {
        example = 'Letter';
      },
    );
  },
);

print(example);

將導致 2 秒后打印Letter

除此之外,forEach 不能包含等待,如 Dart 文檔中所述即使您使用它仍然是異步的。

嘗試使用

for(var document in snapshot.docs){
  //Your code here
}

反而

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM