DispatchGroup 等待 Firestore 查询完成
Posted
技术标签:
【中文标题】DispatchGroup 等待 Firestore 查询完成【英文标题】:DispatchGroup to wait for Firestore query completion 【发布时间】:2022-01-21 03:19:51 【问题描述】:我在单个用户 tapGesture 中运行多个 Firestore 查询,这要求我确保应用程序中同时运行的 Firestore 查询最少或没有。我已经阅读了关于这个问题的多个答案(Waiting until the task finishes),但我的查询没有按照我想要的顺序运行。
感谢您的帮助,指导我使用 DispatchGroup 来确保代码和查询的逻辑顺序。
我想确保 checkAndCreateUserWorkoutProfile 函数中的 Firestore 查询 在 2 个打印语句之前完成在 DispatchGroup.notify(...) 中,以“Firestore 序列”方法开始。相反,查询是在两个打印语句启动后完成的。
下面是我的代码和 Xcode 调试器的截图。如屏幕截图所示,问题在于使用“DispatchGroup.notify(...)”没有等到功能 checkAndCreateUserWorkoutProfile 完成。
alert.addAction(UIAlertAction(title: "Select", style: .default, handler: [self]_ in
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
DispatchQueue.main.async
print("Firestore Sequence 2 Initiated")
startActivityIndicator()
if let user = Auth.auth().currentUser
let userID = user.uid
db.collection("user").whereField("author_uid", isEqualTo: userID).getDocuments snapshot, error in
if error == nil && snapshot != nil
for document in snapshot!.documents
let docID = document.documentID
db.collection("user")
.document(docID)
.setData(["selectedWorkoutID" : workoutRow.workoutId], merge: true)
print("Firestore Sequence 2 Success - user selectedWorkoutID updated")
dispatchGroup.leave()
if let user = Auth.auth().currentUser
let userID = user.uid
checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
dispatchGroup.notify(queue: .main)
print("Firestore Sequence 4 Initiated")
print("Firestore Sequence 5 Initiated - Create/Read User Specific Dayprogram data")
))
func checkAndCreateUserWorkoutProfile(selectedWorkout: Workout, userID: String)
print("Firestore Sequence 3 Initiated - createORread user specific workout profile")
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
db.collection("Workout")
.whereField("workoutId", isEqualTo: selectedWorkout.workoutId)
.whereField("userID", isEqualTo: userID)
.getDocuments() (querySnapshot, err) in
if querySnapshot?.count == 0
var ref: DocumentReference? = nil
ref = self.db.collection("Workout").addDocument(data:
[
"author_uid": selectedWorkout.author_uid!,
"workoutId": selectedWorkout.workoutId,
"userID": userID
])
err in
if let err = err
print("Error adding user specific workout profile: \(err)")
dispatchGroup.leave()
else
print("Firestore Sequence 3 Success - User specific workout profile added/created with ID: \(ref!.documentID)")
dispatchGroup.leave()
根据@Kiril S. 的回答,代码已得到更正,如下所示。
class WorkoutViewController: UIViewController
let dispatchGroup = DispatchGroup()
alert.addAction(UIAlertAction(title: "Select", style: .default, handler: [self]_ in
dispatchGroup.enter()
DispatchQueue.main.async
print("Firestore Sequence 2 Initiated")
startActivityIndicator()
if let user = Auth.auth().currentUser
let userID = user.uid
db.collection("user").whereField("author_uid", isEqualTo: userID).getDocuments snapshot, error in
if error == nil && snapshot != nil
for document in snapshot!.documents
let docID = document.documentID
db.collection("user")
.document(docID)
.setData(["selectedWorkoutID" : workoutRow.workoutId], merge: true)
print("Firestore Sequence 2 Success - user selectedWorkoutID updated")
if let user = Auth.auth().currentUser
let userID = user.uid
checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
dispatchGroup.leave()
dispatchGroup.notify(queue: .main)
print("Firestore Sequence 4 Initiated")
print("Firestore Sequence 5 Initiated - Create/Read User Specific Dayprogram data")
))
func checkAndCreateUserWorkoutProfile(selectedWorkout: Workout, userID: String)
print("Firestore Sequence 3 Initiated - createORread user specific workout profile")
dispatchGroup.enter()
db.collection("Workout")
.whereField("workoutId", isEqualTo: selectedWorkout.workoutId)
.whereField("userID", isEqualTo: userID)
.getDocuments() (querySnapshot, err) in
if querySnapshot?.count == 0
var ref: DocumentReference? = nil
ref = self.db.collection("Workout").addDocument(data:
[
"author_uid": selectedWorkout.author_uid!,
"workoutId": selectedWorkout.workoutId,
"userID": userID
])
err in
if let err = err
print("Error adding user specific workout profile: \(err)")
self.dispatchGroup.leave()
else
print("Firestore Sequence 3 Success - User specific workout profile added/created with ID: \(ref!.documentID)")
self.dispatchGroup.leave()
Xcode 控制台显示更正的代码/查询序列。
【问题讨论】:
【参考方案1】:所以基本上现在发生了什么:
警报操作开始 创建let dispatchGroup = DispatchGroup()
进入该组
离开小组 ==> dispatchGroup.notify
开火
checkAndCreateUserWorkoutProfile
启动,创建自己的调度组等等(已经错了,没关系)
所以你有两个问题:
您必须有 1 个调度组,而不是每个功能的单独组。所以将let dispatchGroup = DispatchGroup()
移动到类成员级别,这样两个函数都在同一个调度组中。
而你的checkAndCreateUserWorkoutProfile
需要进入调度组
在之前的通话离开它。所以顺序应该改成
if let user = Auth.auth().currentUser
let userID = user.uid
checkAndCreateUserWorkoutProfile(selectedWorkout: workoutRow, userID: userID)
dispatchGroup.leave()
这样:
警报操作开始 进入定义为类成员的组 调用checkAndCreateUserWorkoutProfile
,进入同一个调度组并开始异步调用
警报操作离开群组(但不会触发通知,因为checkAndCreateUserWorkoutProfile
尚未离开群组)
checkAndCreateUserWorkoutProfile
最终离开小组 ==> dispatchGroup.notify
火了
【讨论】:
我对@Kiril S. 的详细答案和结构化说明深表感谢,我将上传纠正错误的更新代码。我真的很感激。以上是关于DispatchGroup 等待 Firestore 查询完成的主要内容,如果未能解决你的问题,请参考以下文章
通过 DispatchGroup 与 DispatchQueue 访问主队列
下载 Firebase 存储数据时,DispatchGroup 未正确执行