DispatchGroup 等待 Firestore 查询完成

Posted

技术标签:

【中文标题】DispatchGroup 等待 Firestore 查询完成【英文标题】:DispatchGroup to wait for Firestore query completion 【发布时间】:2022-01-21 03:19:51 【问题描述】:

我在单个用户 tapGesture 中运行多个 Firestore 查询,这要求我确保应用程序中同时运行的 Firestore 查询最少或没有。我已经阅读了关于这个问题的多个答案(Waiting until the task finishes),但我的查询没有按照我想要的顺序运行。

感谢您的帮助,指导我使用 DispatchGroup 来确保代码和查询的逻辑顺序。

我想确保 checkAndCreateUserWorkoutProfile 函数中的 Firestore 查询 在 2 个打印语句之前完成在 DispatchGroup.notify(...) 中,以“Firestore 序列”方法开始。相反,查询是在两个打印语句启动后完成的。

下面是我的代码和 Xcode 调试器的截图。如屏幕截图所示,问题在于使用“DispatchGroup.notify(...)”没有等到功能 checkAndCreateUserWorkoutProfile 完成。

alert.addAction(UIAlertAction(title: "Select", style: .default, handler:  [self]_ in
            
            let dispatchGroup = DispatchGroup()
            
            dispatchGroup.enter()
            
            DispatchQueue.main.async 
                print("Firestore Sequence 2 Initiated")
                startActivityIndicator()
                
                if let user = Auth.auth().currentUser 
                    let userID = user.uid
                    
                    db.collection("user").whereField("author_uid", isEqualTo: userID).getDocuments  snapshot, error in
                        
                        if error == nil && snapshot != nil 
                            
                            for document in snapshot!.documents 
                            
                            let docID = document.documentID
                            
                            db.collection("user")
                                .document(docID)
                                .setData(["selectedWorkoutID" : workoutRow.workoutId], merge: true)
                                    
                            print("Firestore Sequence 2 Success - user selectedWorkoutID updated")
                                
                            
                        
                        dispatchGroup.leave()
                        
                        if let user = Auth.auth().currentUser 
                            let userID = user.uid
                            checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
                        
                    
                
            

            dispatchGroup.notify(queue: .main) 
                print("Firestore Sequence 4 Initiated")
                print("Firestore Sequence 5 Initiated - Create/Read User Specific Dayprogram data")
                

        ))

func checkAndCreateUserWorkoutProfile(selectedWorkout: Workout, userID: String) 

        print("Firestore Sequence 3 Initiated - createORread user specific workout profile")
        
        let dispatchGroup = DispatchGroup()
        
        dispatchGroup.enter()
        
        db.collection("Workout")
            .whereField("workoutId", isEqualTo: selectedWorkout.workoutId)
            .whereField("userID", isEqualTo: userID)
            .getDocuments()  (querySnapshot, err) in
                
                if querySnapshot?.count == 0 

                    var ref: DocumentReference? = nil
                  
                    ref = self.db.collection("Workout").addDocument(data:
                        [
                        "author_uid": selectedWorkout.author_uid!,
                        "workoutId": selectedWorkout.workoutId,
                        "userID": userID
                        ])

                      err in
                        if let err = err 
                            print("Error adding user specific workout profile: \(err)")
                            dispatchGroup.leave()
                         else 
                            print("Firestore Sequence 3 Success - User specific workout profile added/created with ID: \(ref!.documentID)")
                            dispatchGroup.leave()
                        
                    
                    
                    
                 
                
            

        

根据@Kiril S. 的回答,代码已得到更正,如下所示。

class WorkoutViewController: UIViewController 

let dispatchGroup = DispatchGroup()


alert.addAction(UIAlertAction(title: "Select", style: .default, handler:  [self]_ in
            
            dispatchGroup.enter()
            
            DispatchQueue.main.async 
                print("Firestore Sequence 2 Initiated")
                startActivityIndicator()
                
                if let user = Auth.auth().currentUser 
                    let userID = user.uid
                    
                    db.collection("user").whereField("author_uid", isEqualTo: userID).getDocuments  snapshot, error in
                        
                        if error == nil && snapshot != nil 
                            
                            for document in snapshot!.documents 
                            
                            let docID = document.documentID
                            
                            db.collection("user")
                                .document(docID)
                                .setData(["selectedWorkoutID" : workoutRow.workoutId], merge: true)
                                    
                            print("Firestore Sequence 2 Success - user selectedWorkoutID updated")
                                
                            
                        
                        
                        
                        if let user = Auth.auth().currentUser 
                            let userID = user.uid
                            checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
                         dispatchGroup.leave()
                        
                    
                
            

            dispatchGroup.notify(queue: .main) 
                print("Firestore Sequence 4 Initiated")
                print("Firestore Sequence 5 Initiated - Create/Read User Specific Dayprogram data")
                

        ))

func checkAndCreateUserWorkoutProfile(selectedWorkout: Workout, userID: String) 

        print("Firestore Sequence 3 Initiated - createORread user specific workout profile")
        
        dispatchGroup.enter()
        
        db.collection("Workout")
            .whereField("workoutId", isEqualTo: selectedWorkout.workoutId)
            .whereField("userID", isEqualTo: userID)
            .getDocuments()  (querySnapshot, err) in
                
                if querySnapshot?.count == 0 

                    var ref: DocumentReference? = nil
                  
                    ref = self.db.collection("Workout").addDocument(data:
                        [
                        "author_uid": selectedWorkout.author_uid!,
                        "workoutId": selectedWorkout.workoutId,
                        "userID": userID
                        ])

                      err in
                        if let err = err 
                            print("Error adding user specific workout profile: \(err)")
                            self.dispatchGroup.leave()
                         else 
                            print("Firestore Sequence 3 Success - User specific workout profile added/created with ID: \(ref!.documentID)")
                            self.dispatchGroup.leave()
                        
                         
                      
            
        

Xcode 控制台显示更正的代码/查询序列。

【问题讨论】:

【参考方案1】:

所以基本上现在发生了什么:

警报操作开始 创建let dispatchGroup = DispatchGroup() 进入该组 离开小组 ==> dispatchGroup.notify 开火 checkAndCreateUserWorkoutProfile 启动,创建自己的调度组等等(已经错了,没关系)

所以你有两个问题:

    您必须有 1 个调度组,而不是每个功能的单独组。所以将let dispatchGroup = DispatchGroup() 移动到类成员级别,这样两个函数都在同一个调度组中。

    而你的checkAndCreateUserWorkoutProfile需要进入调度组 之前的通话离开它。所以顺序应该改成

    if let user = Auth.auth().currentUser 
        let userID = user.uid
        checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
    
    dispatchGroup.leave()
    

这样:

警报操作开始 进入定义为类成员的组 调用checkAndCreateUserWorkoutProfile,进入同一个调度组并开始异步调用 警报操作离开群组(但不会触发通知,因为checkAndCreateUserWorkoutProfile 尚未离开群组) checkAndCreateUserWorkoutProfile 最终离开小组 ==> dispatchGroup.notify 火了

【讨论】:

我对@Kiril S. 的详细答案和结构化说明深表感谢,我将上传纠正错误的更新代码。我真的很感激。

以上是关于DispatchGroup 等待 Firestore 查询完成的主要内容,如果未能解决你的问题,请参考以下文章

通过 DispatchGroup 与 DispatchQueue 访问主队列

DispatchGroup 逻辑工作流

为啥 DispatchGroup 会干扰主队列?

下载 Firebase 存储数据时,DispatchGroup 未正确执行

- swift:async + JSON + completion + DispatchGroup

如何在执行下一个操作之前等待请求完成(Xcode 11)