火力基地Swift:异步调用,完成处理程序 [英] Firebase & Swift: Asynchronous calls, Completion Handlers

查看:68
本文介绍了火力基地Swift:异步调用,完成处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了很多有关此主题的内容,但仍然对这个特定问题感到困惑.我有许多彼此依赖的Firebase呼叫.这是我的代码的一种简化示例.我无法将其缩短,但仍然无法理解:

I have read up a lot on this subject but have still been stumped on this specific problem. I have many Firebase calls that rely on each other. This is a kind of simplified example of my code. I had trouble making it any shorter and still getting the point across:

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完成后才能运行.这就是为什么我有完成处理程序.根据我的调试打印,我认为这个区域还可以.

This is where the asynchronous calls start. Multiple gatherRankingValues functions can run at one time. This function contains a Firebase call which is asynchronous, which is okay for this function. The updateScores however cannot run until gatherRankingValues is finished. That is why I have the completion handler. I think this area is okay based on my debug printing.

            // 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 rankReceived == false"循环,以防止计算开始.当从数据库接收到值时,我使用完成处理程序在self.rankingAreceived和self.rankingBreceived中进行通知.取而代之的是,计算永远不会发生,并且循环变得无限.

Here is where the main problem comes in. The self.getRankingA and self.getRankingB never run. Both of these methods need to run before the calculation method. I try to put in the "while rankingReceived == false" loop to keep the calculation from starting. I use the completion handler to notify within the self.rankingAreceived and self.rankingBreceived when the values have been received from the database. Instead, the calculation never happens and the loop becomes infinite.

如果我删除了while循环以等待接收排名,则将执行"计算,除非最终结果最终为nil,因为仍然不调用getRankingA和getRankingB方法.

If I remove the while loop waiting for the rankings to be received, the calculations will be "carried out" except the end result ends up being nil because the getRankingA and getRankingB methods still do not get called.


            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的排名以及计算方法在继续执行下一个模因之前运行. 在循环遍历所有模因之后,需要调用collectRankingValues(见下文)的完成,但是 每个排名和计算也要在再次调用循环之前完成 ...如何在getRankingA和getRankingB完成处理程序中告诉模因迭代循环等待?

So yes, every meme gets looped through before the completion is called, but the rankings don't get called. I can't figure out how to get the loop to wait for the rankings from getRankingA and getRankingB and for the calculation method to run before continuing on to the next meme. I need completion of gatherRankingValues (see below) to be called after the loop has been through all the memes, but each ranking and calculation to complete also before the loop gets called again ... How can I within the getRankingA and getRankingB completion handlers tell the meme iterating loop to wait up?

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

}

推荐答案

Tomte的答案解决了我的问题之一(谢谢!).使用以下代码接收到userAranking和userBranking后,将进行计算:

Tomte's answer solved one of my problems (thank you!). The calculations will be carried out after userAranking and userBranking are received with this code:

while let nextMeme = enumerator.nextObject() as? DataSnapshot {
    let group = DispatchGroup()

    group.enter()
    self.getRankingA(userA: userA, memeID: nextMeme.key) {
        self.rankingAreceived = true
        group.leave()
    }

    group.enter()
    self.getRankingB(userB: userB, memeID: nextMeme.key) {
        self.rankingBreceived = true
        group.leave()
    }

    // is called when the last task left the group
    group.notify(queue: .main) {
        self.calculation()
    }

}

仍然,对updateScores的完成调用将在循环结束时发生,但在接收所有userArankings和userBrankings之前以及在对排名进行计算之前.我通过添加另一个调度组解决了这个问题:

Still, the completion call to updateScores would happen at the end of the loop but before all of the userArankings and userBrankings are received and before the rankings undergo calculations. I solved this problem by adding another dispatch group:

let downloadGroup = DispatchGroup()

while let nextMeme = enumerator.nextObject() as? DataSnapshot {
    let calculationGroup = DispatchGroup()

    downloadGroup.enter()    
    calculationGroup.enter()
    self.getRankingA(userA: userA, memeID: nextMeme.key) {
        downloadGroup.leave()
        calculationGroup.leave()
    }

    downloadGroup.enter()
    calculationGroup.enter()
    self.getRankingB(userB: userB, memeID: nextMeme.key) {
        downloadGroup.leave()
        calculationGroup.leave()
    }

    // is called when the last task left the group
    downloadGroup.enter()
    calculationGroup.notify(queue: .main) {
        self.calculation() {
            downloadGroup.leave()
        }
    }

}

downloadGroup.notify(queue: .main) {
    completion(self, userA, userB)
}

我还必须在计算方法中添加一个完成处理程序,以确保一旦userAranking和userBranking进行了计算,而不是从数据库中接收到它们之后,就会调用updateScores方法.

I had to add a completion handler to the calculation method as well to ensure that the updateScores method would be called once userAranking and userBranking undergo calculations, not just once they are received from the database.

同意派遣小组!

这篇关于火力基地Swift:异步调用,完成处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆