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

如何让函数等待数据库响应后返回值?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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 13:05:24