You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免Firebase数据加载前调用Completion Handler?代码求助

问题解决:让Completion Handler等Firebase数据全加载完再调用

嘿,我来给你掰扯明白这个事儿,保证像给5岁娃讲一样简单!

为啥现在Completion跑太早了?

你现在的代码就像:

你让5个小朋友分别去厨房拿一颗糖,刚说完让他们出发,你就立刻伸手问:“糖呢?”

这里的userRef.child(item.key).observeSingleEvent就是让小朋友跑厨房拿糖——这是异步操作,意思是小朋友不会立刻把糖给你,他们得跑一趟(Firebase服务器返回数据)才能回来。而你现在循环完所有小朋友的任务后,马上就调用completion(users),这时候所有小朋友还在半路上呢,users数组当然是空的啦!

怎么用“等待小组”(DispatchGroup)解决?

我们需要一个“小队长”来盯着所有小朋友,等所有小朋友都拿着糖回来,再喊你过来收糖。这个“小队长”就是DispatchGroup:

  1. 先创建一个小组:就像让小队长站好等着。
  2. 每个小朋友出发前,小队长记下来“哦,又出去一个”(调用group.enter())。
  3. 每个小朋友拿糖回来后,告诉小队长“我回来啦”(调用group.leave())。
  4. 小队长等所有人都回来,就喊你:“所有人都到齐啦,来收糖!”(调用group.notify,在里面执行completion)。

修改后的完整代码

class func fetchFavorites(userId: String, completion: @escaping ([User]) -> Swift.Void) { 
    let userRef = Database.database().reference().child("users") 
    let favRef = Database.database().reference().child("favorites").child(userId) 
    favRef.observe(.value) { snap in 
        var users: [User] = [] 
        // 1. 创建我们的"小队长"
        let dispatchGroup = DispatchGroup()
        
        for item in snap.children.allObjects as! [DataSnapshot] { 
            // 2. 每个小朋友出发前,告诉小队长"我出去啦"
            dispatchGroup.enter()
            
            userRef.child(item.key).observeSingleEvent(of: .value) { snapshot in 
                defer {
                    // 4. 不管拿到糖没拿到,都告诉小队长"我回来啦"
                    // defer的意思是:不管这个闭包里的代码成功还是失败,最后都会执行这行
                    dispatchGroup.leave()
                }
                
                if let dictionary = snapshot.value as? [String:Any] { 
                    let newUser = User(dictionary: dictionary) 
                    users.append(newUser) 
                } 
            } 
        }
        
        // 3. 小队长等所有人回来,就喊你收糖
        dispatchGroup.notify(queue: DispatchQueue.main) {
            completion(users)
        }
    } 
}

额外小提醒

  • 用defer来调用group.leave()是个好习惯,这样哪怕某个请求出错了(比如拿不到用户数据),小队长也能收到“小朋友回来”的通知,不会一直等下去。
  • 我们把completion放在dispatchGroup.notify里,而且指定了queue: .main,这样回调会在主线程执行,方便你更新UI哦!

内容的提问来源于stack exchange,提问作者p3scobar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:37:19