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

Firebase Firestore查询返回空数组,请求排查时序问题

解决Firestore异步查询导致数组为空的问题

你的推测完全正确——这就是异步代码的时序问题。Firestore的retrieve方法(包括内部的addSnapshotListener)是异步执行的:它会在后台线程发起数据请求,等数据返回后才会触发你传入的completion闭包。而你当前的getCollectionExpenses函数里,调用setExpenses()后立刻打印expenses,这时候异步请求还没完成,self.expenses自然还是空数组。

问题代码的时序拆解

我们一步步捋清楚执行顺序:

  1. 调用getCollectionExpenses
  2. 执行setExpenses(),内部触发Firestore的异步查询请求
  3. 同步执行print(expenses)和print("hello")——此时Firestore还没返回数据,expenses是空的
  4. 一段时间后,Firestore数据返回,retrieve的completion闭包执行,self.expenses = expenses才会被赋值

正确的解决方法:用闭包控制异步流程

你需要把所有依赖异步数据的逻辑,放到数据返回后的回调里执行,而不是同步执行。这里有两种清晰的修改方案:

方案一:给setExpenses添加completion闭包

先修改setExpenses,让它能通知调用者数据已经准备好:

func setExpenses(completion: @escaping () -> Void) {
    FirebaseFunctions().retrieve(from: .expense, username: username as! String, returning: Expenses.self) { (expenses) in
        self.expenses = expenses
        completion() // 数据赋值完成,通知上层
    }
}

然后调整getCollectionExpenses,等待数据准备好后再处理:

public func getCollectionExpenses(collection: String, completionHandler: @escaping([Expenses], [String]) -> Void){
    setExpenses {
        // 这里才是数据已经加载完成的时机
        print(self.expenses)
        print("hello")
        
        var expensePerCollection = [Expenses]()
        for eachExpense in self.expenses {
            if eachExpense.collection == collection {
                expensePerCollection.append(eachExpense)
            }
        }
        
        // 把处理结果通过completion返回给调用者
        completionHandler(expensePerCollection, /* 这里传入你需要的[String]类型参数 */)
    }
}

方案二:直接调用retrieve,跳过中间封装

如果setExpenses的作用只是给self.expenses赋值,你可以直接在getCollectionExpenses里调用retrieve,这样能更直接地控制异步流程:

public func getCollectionExpenses(collection: String, completionHandler: @escaping([Expenses], [String]) -> Void){
    FirebaseFunctions().retrieve(from: .expense, username: username as! String, returning: Expenses.self) { (expenses) in
        self.expenses = expenses
        
        // 直接在这里处理筛选逻辑
        var expensePerCollection = [Expenses]()
        for eachExpense in expenses {
            if eachExpense.collection == collection {
                expensePerCollection.append(eachExpense)
            }
        }
        
        print(expenses) // 这里打印的就是有数据的数组了
        print("hello")
        
        completionHandler(expensePerCollection, /* 传入对应的[String]参数 */)
    }
}

关键注意事项

  • 永远不要假设异步代码会同步完成,所有依赖异步数据的逻辑必须放到completion闭包或回调里
  • 如果需要更新UI,记得用DispatchQueue.main.async把UI操作切回主线程(Firestore的completion闭包默认在后台线程执行)

内容的提问来源于stack exchange,提问作者Arnav GUPTA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:27:46