如何正确调用DispatchGroup?Firebase多标签UID聚合后执行函数
解决DispatchGroup配合Firebase异步请求的问题
你的问题核心是没把DispatchGroup的enter()和leave()和Firebase的异步请求正确绑定起来——因为Firebase的observe(.value)是异步执行的,forEach循环会快速走完所有迭代,但每个请求的回调是独立触发的,之前的写法没把这些异步任务纳入DispatchGroup的管理,导致fetchTeams被触发了两次。
正确的调用位置应该是这样:
- 在每个异步请求发起前调用
enter():也就是在forEach遍历每个标签时,刚拿到key就调用group.enter(),告诉DispatchGroup“我要开始一个新的异步任务了”。 - 在异步请求的回调结束时调用
leave():在Firebase回调里,把UID添加到数组的操作完成后,调用group.leave(),告诉DispatchGroup“这个任务完成了”。 - 在所有迭代之外设置
notify:等所有enter的任务都leave后,notify里的闭包就会执行,这时候再调用fetchTeams,就能保证只执行一次,而且数组是完整的。
修正后的代码如下:
func filterTempo(filterTag: FilteredTags) { let group = DispatchGroup() let tag = filterTag.selectedTag.components(separatedBy: "/") // 返回"NewYork"和"baseball" // 先清空数组,避免之前的数据干扰 self.arrays.removeAll() tag.forEach { (key) in // 每个标签对应一个异步任务,先标记任务开始 group.enter() self.ref.child("tags").child(key).observe(.value, with: { (snapshot) in defer { // 用defer确保不管解析是否成功,都能标记任务完成,避免group卡死 group.leave() } guard let dictionary = snapshot.value as? [String:Any] else { return } for (uid,_) in dictionary { self.arrays.append(uid) } }, withCancel: { _ in // 如果请求被取消,也要标记任务完成 group.leave() }) } // 所有异步任务完成后,在主队列执行fetchTeams group.notify(queue: .main) { // 可选:如果需要去重,可以转成Set再转回数组 // let uniqueUids = Array(Set(self.arrays)) // self.fetchTeams(array: uniqueUids) self.fetchTeams(array: self.arrays) } }
另外提个小建议:你的数组里会出现重复的UID(比如uid1在两个标签里都存在),如果业务上需要唯一的UID列表,可以把数组转成Set再转回数组,这样传给fetchTeams的就是去重后的结果了。
内容的提问来源于stack exchange,提问作者Daibaku
相关产品推荐
相关产品推荐

