从Firebase取数据时执行顺序异常,如何正确返回填充后的User对象?
解决Firebase异步查询后返回填充完成的User对象问题
嘿,我明白你遇到的问题了——Firebase的observeSingleEvent是异步操作,它会在后台线程去获取数据库数据,而你的主线程不会等它完成,直接就执行了return user,这时候User对象还没被赋值,自然返回的是初始的空值。终端里的输出顺序也印证了这点:先打印了Execute return和空User,之后异步回调才拿到数据并打印出正确的邮箱和组信息。
下面给你两种靠谱的解决方案,选哪种取决于你的Swift版本:
方案一:使用闭包(Completion Handler)
这是兼容所有Swift版本的经典做法,我们把函数的返回逻辑改成通过闭包回调来传递填充好的User对象:
class DatabaseQueries { var ref: DatabaseReference? // 修改函数,添加completion回调参数 func findUserInfo(completion: @escaping (User?, Error?) -> Void) { // 先检查当前用户是否存在,避免强制解包崩溃 guard let userID = Auth.auth().currentUser?.uid else { let error = NSError(domain: "AuthError", code: -1, userInfo: [NSLocalizedDescriptionKey: "没有当前登录用户"]) completion(nil, error) return } ref = Database.database().reference() ref?.child("Users").child(userID).observeSingleEvent(of: .value, with: { snapshot in // 在异步回调里解析数据并创建User对象 let value = snapshot.value as? NSDictionary let group = value?["group"] as? String ?? "" let email = value?["mail"] as? String ?? "" let filledUser = User(email: email, group: group) // 通过completion返回填充好的User completion(filledUser, nil) }) { error in // 传递错误信息 completion(nil, error) } } }
调用方式:
let dbQueries = DatabaseQueries() dbQueries.findUserInfo { user, error in if let error = error { print("获取用户信息失败:\(error.localizedDescription)") return } if let user = user { print("获取到的用户:\(user)") // 在这里使用已经填充好的User对象 } }
方案二:使用Async/Await(Swift 5.5+)
如果你用的是Swift 5.5或更高版本,推荐用async/await语法,代码会更简洁易读:
class DatabaseQueries { var ref: DatabaseReference? // 把函数声明为async throws,支持异步抛出错误 func findUserInfo() async throws -> User { guard let userID = Auth.auth().currentUser?.uid else { throw NSError(domain: "AuthError", code: -1, userInfo: [NSLocalizedDescriptionKey: "没有当前登录用户"]) } ref = Database.database().reference() // 使用await等待异步操作完成 let snapshot = try await ref?.child("Users").child(userID).observeSingleEvent(of: .value) guard let value = snapshot?.value as? NSDictionary else { throw NSError(domain: "DataError", code: -2, userInfo: [NSLocalizedDescriptionKey: "用户数据格式无效"]) } let group = value["group"] as? String ?? "" let email = value["mail"] as? String ?? "" return User(email: email, group: group) } }
调用方式:
需要在Task里调用异步函数:
Task { do { let dbQueries = DatabaseQueries() let user = try await dbQueries.findUserInfo() print("获取到的用户:\(user)") // 在这里使用填充好的User对象 } catch { print("出错了:\(error.localizedDescription)") } }
核心原因总结
你的原始代码之所以顺序异常,是因为Firebase的数据库操作是非阻塞式的——主线程不会等待异步任务完成,而是继续执行后续代码。所以必须用异步编程的方式(闭包或async/await),等到数据真正获取到之后,再处理和返回User对象。
内容的提问来源于stack exchange,提问作者Theetje
相关产品推荐
相关产品推荐

