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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 15:40:22