Swift - ожидание завершения асинхронного цикла for-in перед вызовом обработчика завершения swift - PullRequest
0 голосов
/ 22 января 2019

Я озадачен тем, что является лучшим способом для достижения этой цели.Я пытаюсь сохранить промежуточный итог значений Double, которые я перебираю и складываю вместе с помощью сетевого вызова.Все, что я прочитал, говорит об использовании DispatchGroup.Мое завершение либо вызывает слишком рано, либо вообще не вызывается, и я попробовал каждую конфигурацию .enter, .leave и .wait, о которой я могу подумать.

    let group = DispatchGroup()
    var runningTotal: Double = 0.00

    ref.observeSingleEvent(of: .value) { (snapshot) in
        guard let bills = snapshot.value as? [String: AnyObject] else {
            //error
            return
        }

        for billId in bills.keys {
            group.enter()
            print("Entering")
            Database.database().reference().child("bills").child(billId).observeSingleEvent(of: .value, with: { (snapshot) in
                guard let bill = snapshot.value as? [String: AnyObject] else {
                    return
                }
                if let amount = bill["amount"] as? Double {
                    runningTotal += amount
                }
                group.leave()
                print("Leaving")
            })
        }
        completion(runningTotal)
    }
    group.wait()
}

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Пара мыслей:

  1. Старайтесь не звонить wait из основного потока.Варианты использования для этого довольно ограничены.notify - гораздо более безопасный способ достижения того же.

  2. Убедитесь, что вы вызываете leave с каждого пути внутри петли.Этого можно добиться с помощью блока defer.

Итак:

func foo(completion: @escaping (Double?) -> Void) {
    ref.observeSingleEvent(of: .value) { snapshot in
        guard let bills = snapshot.value as? [String: AnyObject] else {
            //error
            completion(nil)
            return
        }

        let group = DispatchGroup()
        var runningTotal = 0.0

        for billId in bills.keys {
            group.enter()
            print("Entering")
            Database.database().reference().child("bills").child(billId).observeSingleEvent(of: .value) { snapshot in
                defer { group.leave() }
                guard let bill = snapshot.value as? [String: AnyObject] else {
                    return
                }
                if let amount = bill["amount"] as? Double {
                    runningTotal += amount
                }
                print("Leaving")
            }
        }
        group.notify(queue: .main) {
            completion(runningTotal)
        }
    }
}
0 голосов
/ 22 января 2019

Необходимо дождаться выполнения всех групповых задач, затем вызвать блок завершения.

Как показано ниже.

    var runningTotal: Double = 0.00

    ref.observeSingleEvent(of: .value) { (snapshot) in
        guard let bills = snapshot.value as? [String: AnyObject] else {
            //error
            return
        }

        let group = DispatchGroup()
        for billId in bills.keys {
            group.enter()
            print("Entering")
            Database.database().reference().child("bills").child(billId).observeSingleEvent(of: .value, with: { (snapshot) in
                guard let bill = snapshot.value as? [String: AnyObject] else {
                    group.leave()
                    return
                }
                if let amount = bill["amount"] as? Double {
                    runningTotal += amount
                }
                group.leave()
                print("Leaving")
            })
        }
        group.wait()
        completion(runningTotal)
    }
...