【问题标题】:Waiting for loop to finish with DispatchGroup getting data from firebase using Swift等待循环完成,DispatchGroup 使用 Swift 从 firebase 获取数据
【发布时间】:2020-09-21 16:14:11
【问题描述】:

我正在尝试获取一组员工并等待它完成。

尝试使用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)
            
        }

【问题讨论】:

    标签: arrays swift firebase swiftui


    【解决方案1】:

    问题在于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 条件中提供了第一个错误。再说一遍,随心所欲。

    【讨论】:

    • 非常感谢您的解决方案和解释!现在可以了!快速提问,不建议使用这种调度组方法吗?因为它阻塞了主线程。有没有更好的方法来实现这个目标?
    • @ElaiZuberman 因为您使用的是notify,所以您没有阻止任何内容。当然,我们可以提出其他改进建议,但我在上面的 cmets 中解决了我认为的实质性问题。
    【解决方案2】:

    不要这样做,因为你阻塞了主线程,使它对所有东西都没有响应,而是在准备好时异步调度结果,比如

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

    【讨论】:

    • 但是这个方法不会让我在获取完成时使用完成块。
    • 为什么在这个用例中需要补全?使用上述方法,您的用户将在一些数据出现后立即在 UI 中获得一些更新,而不是在完成时的最后。
    • 因为我想在它完成后再次循环使用这个数组。我已经添加了我想应用于这个问题的工作。我觉得这是完成这项任务所需的唯一方法。当然可能有更好的方法,但现在我认为会这样做。
    猜你喜欢
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 2012-09-30
    • 2015-06-24
    • 1970-01-01
    • 2017-05-03
    • 2020-11-10
    相关资源
    最近更新 更多