如何让getStudents等待addSnapshotListener内代码执行完毕再返回?
解决方案
方案1:改用单次数据获取(适合仅需一次加载的场景)
如果你的需求只是一次性获取学生数据,而非实时监听数据变化,直接替换addSnapshotListener为getDocuments,结合async/await(iOS 15+、Swift 5.5+)实现同步等待:
func getStudents() async { do { let querySnapshot = try await db.collection(StudentViewModel.studentCollection).getDocuments() var updatedStudentDocuments: [Student] = [] for studentDocument in querySnapshot.documents { let student = Student( id: studentDocument.documentID, name: studentDocument.data()["name"] as? String ?? "" ) updatedStudentDocuments.append(student) } DispatchQueue.main.async { self.students = updatedStudentDocuments } } catch { print("ERROR | Getting Student Documents in Firestore Service: \(error)") } }
调用示例(需在异步上下文执行):
await getStudents() // 此时self.students已完成更新
方案2:保留实时监听,等待首次回调完成
如果必须保留addSnapshotListener的实时监听能力,同时要等待首次回调完成后再继续执行,可使用DispatchGroup:
func getStudents(completion: @escaping () -> Void) { let group = DispatchGroup() group.enter() var listener: ListenerRegistration? listener = db.collection(StudentViewModel.studentCollection).addSnapshotListener { [weak self] (querySnapshot, error) in guard let self = self else { group.leave() return } guard error == nil else { print("ERROR | Getting Student Documents in Firestore Service: \(String(describing: error))") group.leave() return } guard let snapshot = querySnapshot else { print("No documents to fetch!") group.leave() return } var updatedStudentDocuments: [Student] = [] for studentDocument in snapshot.documents { let student = Student( id: studentDocument.documentID, name: studentDocument.data()["name"] as? String ?? "" ) updatedStudentDocuments.append(student) } DispatchQueue.main.async { self.students = updatedStudentDocuments group.leave() // 若不需要持续监听,可在此移除监听 // listener?.remove() } } // 等待首次回调完成(注意:不要在主线程调用,避免UI卡死) group.wait() completion() }
关键说明
addSnapshotListener的设计初衷是实时监听数据变化,会在数据更新时多次触发回调,若仅需单次加载数据,优先使用getDocuments更合理。- 异步操作尽量避免强制阻塞线程,推荐用
async/await或completionHandler的方式处理,保证线程不被阻塞,尤其是主线程。
内容的提问来源于stack exchange,提问作者Amine BenJ
相关产品推荐
相关产品推荐

