Я много читал на эту тему, но все еще был озадачен этой конкретной проблемой. У меня есть много вызовов Firebase, которые полагаются друг на друга. Это своего рода упрощенный пример моего кода. У меня были проблемы с тем, чтобы сделать это немного короче, но я все еще понимал:
class ScoreUpdater {
static let ref = Database.database().reference()
var userAranking = Int?
var userBranking = Int?
var rankingAreceived = false
var rankingBreceived = false
var sum = 0
// Pass in the current user and the current meme
static func beginUpdate(memeID: String, userID: String) {
// Iterate through each user who has ranked the meme before
ScoreUpdater.ref.child("memes/\(memeID)/rankings")observeSingleEvent(of: .value) {
let enumerator = snapshot.children
while let nextUser = enumerator.nextObject() as? DataSnapshot {
// Create a currentUpdater instance for the current user paired with each other user
let currentUpdater = ScoreUpdater()
Здесь начинаются асинхронные вызовы. Несколько функций collectRankingValues могут запускаться одновременно. Эта функция содержит асинхронный вызов Firebase, что нормально для этой функции. Однако updateScores не может быть запущен, пока collectRankingValues не будет завершен. Вот почему у меня есть обработчик завершения. Я думаю, что эта область в порядке, основываясь на моей отладочной печати.
// After gatherRankingValues is finished running,
// then updateScores can run
currentUpdater.gatherRankingValues(userA: userID, userB: nextUser.key as! String) {
currentUpdater, userA, userB in
currentUpdater.updateScores(userA: userA, userB:userB)
}
}
}
}
func gatherRankingValues(userA: String, userB: String, completion: @escaping (_ currentUpdater: SimilarityScoreUpdater, _ userA: String, _ userB: String) -> Void) {
// Iterate through every meme in the database
ScoreUpdater.ref.child("memes").observeSingleEvent(of: .value) {
snapshot in
let enumerator = snapshot.children
while let nextMeme = enumerator.nextObject() as? DataSnapshot {
Вот здесь и возникает основная проблема. Self.getRankingA и self.getRankingB никогда не запускаются. Оба эти метода должны быть запущены до метода расчета. Я пытаюсь вставить цикл «while RankingReceived == false», чтобы не начинать вычисления. Я использую обработчик завершения для уведомления в self.rankingAreceived и self.rankingBreceived о получении значений из базы данных. Вместо этого вычисление никогда не происходит, и цикл становится бесконечным.
Если я уберу цикл while, ожидающий получения ранжирования, вычисления будут «выполнены», за исключением того, что конечный результат будет равен нулю, поскольку методы getRankingA и getRankingB по-прежнему не вызываются.
self.getRankingA(userA: userA, memeID: nextMeme.key) {
self.rankingAreceived = true
}
self.getRankingB(userB: userB, memeID: nextMeme.key) {
self.rankingBreceived = true
}
while self.rankingAreceived == false || self.rankingBreceived == false {
continue
}
self.calculation()
}
Так что да, каждый мем проходит через весь цикл до вызова завершения, но рейтинг не вызывается. Я не могу понять, как заставить цикл ждать ранжирования от getRankingA и getRankingB и запустить метод расчета, прежде чем перейти к следующему мему. Мне нужно завершить of collectRankingValues (см. ниже) для вызова после прохождения цикла через все мемы, но для каждого ранжирования и вычисления также необходимо завершить до повторного вызова цикла ... Как можно Я в обработчиках завершения getRankingA и getRankingB приказываю циклу итерации мема ждать?
// After every meme has been looped through for this pair of users, call completion
completion(self, userA, userB)
}
}
function getRankingA(userA: String, memeID: String, completion: @escaping () -> Void) {
ScoreUpdater.ref.child("memes/\(memeID)\rankings\(userA)").observeSingleEvent(of: .value) {
snapshot in
self.userAranking = snapshot.value
completion()
}
}
function getRankingB(userB: String, memeID: String, completion: @escaping () -> Void) {
ScoreUpdater.ref.child("memes/\(memeID)\rankings\(userB)").observeSingleEvent(of: .value) {
snapshot in
self.userBranking = snapshot.value
completion()
}
}
func calculation() {
self.sum = self.userAranking + self.userBranking
self.userAranking = nil
self.userBranking = nil
}
func updateScores() {
ScoreUpdater.ref.child(...)...setValue(self.sum)
}
}