如何让函数等待数据库响应后返回值?DispatchGroup失效问题
问题排查与修复
DispatchGroup失效的核心原因
你把group.leave()放在了Firebase数据库异步请求的闭包外面,这会导致每次调用group.enter()后立刻执行leave(),DispatchGroup会错误地认为所有任务已经完成,直接触发notify回调,完全没等待数据库请求的异步响应。
修复方案
把group.leave()移到数据库请求完成后的闭包内部,确保只有当异步请求结束(无论成功还是失败)时才调用leave(),这样DispatchGroup才能正确等待所有请求完成。同时,数据库请求的闭包可能在后台线程执行,修改chatrooms数组时要注意线程安全,避免多线程写入冲突。
修复后的代码:
func getUserChatRoom(chatroomsID: [String], handler: @escaping(_ chatrooms: [ChatRoomModel]) -> ()) { guard !chatroomsID.isEmpty else { return handler([]) } print("1") let group = DispatchGroup() let queue = DispatchQueue(label: "com.chatroom.queue") // 串行队列保证线程安全 var chatrooms: [ChatRoomModel] = [] for id in chatroomsID { print("2") group.enter() print("3") db.collection("chatrooms").document(id).getDocument { (document, error) in defer { group.leave() // 用defer确保无论成功失败都会调用leave } if let doc = document, doc.exists, let chatroom = self.getChatRoomModel(document: doc) { print("4") queue.sync { // 用串行队列同步写入,避免线程冲突 chatrooms.append(chatroom) } print(chatrooms.count) } else if let error = error { print(error) } } print("5") } group.notify(queue: .main) { print("所有请求完成") handler(chatrooms) } }
关键说明
defer的使用:在闭包开头加defer { group.leave() },可以保证不管请求成功还是失败,leave()都会被调用,避免出现DispatchGroup永久等待的情况(比如请求出错时忘记调用leave)。- 线程安全:Firebase的
getDocument闭包默认在后台线程执行,如果多个闭包同时修改chatrooms数组,会引发线程安全问题。用串行队列的sync方法写入数组,能保证同一时间只有一个线程修改数组。
内容的提问来源于stack exchange,提问作者Hassan H'Sheaib
相关产品推荐
相关产品推荐

