簡體   English   中英

等待循環完成 DispatchGroup 使用 Swift 從 firebase 獲取數據

[英]Waiting for loop to finish with DispatchGroup getting data from firebase using Swift

我正在嘗試獲取一系列員工並等待它完成。

嘗試使用DispatchGroup但似乎什么也沒發生。

有二維數組,第一個是在員工上方,第二個是在他成功收到的文件上。 然后,它應該將ShiftConst對象附加到請求的數組中。

我希望它通過使用前面的完成塊來通知它何時完成。

我得到的結果是 0 並且“沒有完成”。

這是我的代碼:


    @Published var employeesConst = [ShiftConst]()

    func getAllShiftConsts(employees: [Employee], completion: @escaping (Bool) -> () = {_ in}){
        
        let group = DispatchGroup()
        var finish = false
        
        for employee in employees{
            let ref = self.session.db.collection(CollectionRef.users.rawValue).document(employee.uid!).collection(CollectionRef.shiftConsts.rawValue)
            
            ref.getDocuments { (qSnapshot, error) in
                
                guard let qSnapshot = qSnapshot, !qSnapshot.isEmpty else {return}
                
                for document in qSnapshot.documents{
                    group.enter()
                    do{
                        let data = try document.decode(as: ShiftConst.self)
                        self.employeesConst.append(data)
                        finish = true
                        group.leave()
                    }catch let error{
                        finish = false
                        group.leave()
                        print(error)
                    }
                    
                }
            }
        }
        
        group.notify(queue: .main) {
            completion(finish)
        }
    }

用法:

getAllShiftConsts(employees: session.employeesList) { finish in
            if finish {
                print("finish")
                print("Count: ", self.employeesConst.count)
            }else{
                print("didn't finish")
                print("Count: ", self.employeesConst.count)
            }
        }

完成后我想用它來做這個:

for (index, symbol) in self.date.weekDaySymbols.enumerated(){
            
            let day = Day(date: self.date.nextWeek[index], isSelected: false, hasShifts: false, daySymbol: symbol, dayNumber: self.date.nextWeekNumbers[index])
            
            day.setShifts(morning: self.session.employeesList, middle: self.session.employeesList, evening: self.session.employeesList)
            
            for const in self.employeesConst{
                if const.dateToString() == day.date{
                    for shift in day.shifts{
                        if shift.type == const.shiftType{
                            shift.filterEmployees(shiftConsts: const)
                        }
                    }
                }
            }
            
            if index == 0 {day.isSelected = true}
            
            self.week.append(day)
            
        }

不要這樣做,因為您阻塞了主線程,使其對所有內容都沒有響應,而是在准備好時異步分派結果,例如

func getAllShiftConsts(employees: [Employee], completion: @escaping (Bool) -> () = {_ in}){
    
    for employee in employees{
        let ref = self.session.db.collection(CollectionRef.users.rawValue).document(employee.uid!).collection(CollectionRef.shiftConsts.rawValue)
        
        ref.getDocuments { (qSnapshot, error) in
            guard let qSnapshot = qSnapshot, !qSnapshot.isEmpty else {return}
            for document in qSnapshot.documents{
                do{
                    let data = try document.decode(as: ShiftConst.self)
                   DispatchQueue.main.async {
                     self.employeesConst.append(data)
                   }
                }catch let error{
                    print(error)
                }
            }
        }
    }
}

問題在於, notify確定到目前為止它遇到的所有enter調用是否都被等量的leave調用抵消了。 但是您的enter調用異步ref.getDocuments調用中,因此無疑會在到達第一個enter之前到達notify 並且,因此,它會立即觸發notify塊,根本不等待。

一個更小的觀察結果是您還在該for循環中使用了組,但這不是必需的,除非您在該循環內執行任何異步操作。 因此,除非您的for循環觸發了額外的異步任務,否則我不會將它與組糾纏在一起。

因此,最重要的是,在異步調用之前調用enter ,並在調用完成后leave (在這種情況下最好使用defer實現,恕我直言)。

例如

func getAllShiftConsts(employees: [Employee], completion: ((Result<[ShiftConst], Error>) -> Void)? = nil) {
    let group = DispatchGroup()
    var employees: [ShiftConst] = []
    var errors: [Error] = []
    
    for employee in employees {
        let ref = session.db.collection(CollectionRef.users.rawValue).document(employee.uid!).collection(CollectionRef.shiftConsts.rawValue)

        group.enter()
        
        ref.getDocuments { qSnapshot, error in
            defer { group.leave() }
            
            guard let qSnapshot = qSnapshot, !qSnapshot.isEmpty else { return }
            
            for document in qSnapshot.documents {     
                do {
                    let data = try document.decode(as: ShiftConst.self)
                    employees.append(data)
                } catch let error {
                    errors.append(error)
                }
            }
        }
    }
    
    group.notify(queue: .main) {
        if let error = errors.first {
            completion?(.failure(error))       // I don't know what you want to do with all the errors, so I'll just grab the first one
        } else {
            completion?(.success(employees))
        }
    }
}

上面的其他一些小的修改:

  1. 我建議不要在這個for循環中更新self.employeesConst 您通常不希望有一個異步過程更新模型對象。 您通常需要明確的職責分離,此函數負責檢索結果數組,但不應更新模型對象。 我會將數組構建為局部變量並將結果傳遞回閉包中。

  2. 如果閉包是可選的,而不是將其默認為{ _ in } ,您實際上會將其設為可選,並使用completion?(...)語法調用它。 就個人而言,我不會將其設為可選(以便調用者負責更新模型),但我將其設為可選以匹配您的代碼示例。

  3. 我不知道在出現錯誤的情況下你想做什么,所以我只是構建了一個錯誤數組並在.failure條件下提供了第一個錯誤。 再次,做你想做的。

暫無
暫無

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

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