Firestore数据获取代码为何返回空数组?求优化方案
问题解答:Firestore异步请求导致的执行顺序问题及优化方案
执行顺序异常的原因
Firestore的getDocument是异步网络操作,它不会阻塞当前执行线程(通常是主线程)。当你在for循环中调用getDocument时,代码只是发起了网络请求,会立刻继续执行循环外部的同步逻辑(也就是print("5")和print("6"));而闭包内的代码(print("1")至print("4"))要等Firestore服务器返回数据后才会被执行,这就是你看到5、6先打印的核心原因。
另外你当前用games.count == joinedGamesIDs.count触发handler的逻辑存在严重隐患:如果任意一个请求失败(比如文档不存在、网络中断),games的元素数量永远无法达到目标值,handler会永远不会被调用,导致调用方一直处于等待状态。
优化解决方案
方案1:使用DispatchGroup(兼容iOS 13+)
通过DispatchGroup可以精确追踪所有异步请求的完成状态,无论请求成功或失败,都会标记任务完成,确保所有请求结束后统一触发handler:
func getMyGames(joinedGamesIDs: [String], handler: @escaping(_ games: [GameViewModal]) -> ()) { var games = [GameViewModal]() guard !joinedGamesIDs.isEmpty else { handler(games) return } let group = DispatchGroup() for id in joinedGamesIDs { group.enter() // 标记任务开始 db.collection("games").document(id).getDocument { (document, error) in defer { group.leave() // 无论成功失败,最终标记任务结束 } if let document = document, document.exists { if let game = self.getGameViewModal(document: document){ games.append(game) } } // 可在此添加错误处理逻辑,比如记录错误日志 } } // 所有任务完成后,在主线程触发handler group.notify(queue: .main) { handler(games) } }
方案2:使用Async/Await(iOS 15+/macOS 12+)
Swift的并发模型让异步代码更简洁易读,Firebase 9及以上版本已原生支持Async/Await,无需手动包装:
// 重写为Async函数 func getMyGames(joinedGamesIDs: [String]) async -> [GameViewModal] { var games = [GameViewModal]() guard !joinedGamesIDs.isEmpty else { return games } for id in joinedGamesIDs { do { let document = try await db.collection("games").document(id).getDocument() if document.exists, let game = getGameViewModal(document: document) { games.append(game) } } catch { print("获取游戏数据失败:\(error)") // 可在此添加错误处理逻辑 } } return games }
调用示例:
Task { let myGames = await getMyGames(joinedGamesIDs: yourGameIDsArray) // 处理获取到的游戏数组 }
内容的提问来源于stack exchange,提问作者Hassan H'Sheaib
相关产品推荐
相关产品推荐

