for 循环中的异步调用会触发两次完成
Asynchronous call in for loop fires two completions
我有一个检索数据的方法。此数据可以但 不必 包含图像。下面的方法是 按顺序 检索数据,这对应用程序至关重要:
static func getWishes(dataSourceArray: [Wishlist], completion: @escaping (_ success: Bool, _ dataArray: [Wishlist]) -> Void){
var dataSourceArrayWithWishes = dataSourceArray
let db = Firestore.firestore()
let userID = Auth.auth().currentUser!.uid
var j = 0
let dispatchSemaphore = DispatchSemaphore(value: 0)
let dispatchSemaphoreOuter = DispatchSemaphore(value: 0)
let dispatchQueue1 = DispatchQueue(label: "taskQueue")
var listCounter = 0
dispatchQueue1.async {
for list in dataSourceArray {
listCounter += 1
db.collection("users").document(userID).collection("wishlists").document(list.name).collection("wünsche").order(by: "wishCounter").getDocuments() { ( querySnapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
// dispatch group to make sure completion only fires when for loop is finished
// append every Wish to array at wishIDX
var i = 0
let dispatchQueue = DispatchQueue(label: "taskQueue1")
if (querySnapshot?.documents.count)! > 0 {
dispatchQueue.async {
for document in querySnapshot!.documents {
let documentData = document.data()
let imageUrlString = document["imageUrl"] as? String ?? ""
if let imageUrl = URL(string: imageUrlString) {
KingfisherManager.shared.retrieveImage(with: imageUrl, options: nil, progressBlock: nil, completionHandler: { result in
var image = UIImage()
switch result {
case .success(let abc):
image = abc.image
case .failure(let error):
print(error)
break
}
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: image))
i = i + 1
if i == querySnapshot?.documents.count {
j = j + 1
dispatchSemaphoreOuter.signal()
dispatchSemaphore.signal()
if j == dataSourceArray.count {
print("completion1")
completion(true, dataSourceArrayWithWishes)
}
} else {
dispatchSemaphore.signal()
}
})
} else {
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: image))
i = i + 1
if i == querySnapshot?.documents.count {
j = j + 1
dispatchSemaphoreOuter.signal()
dispatchSemaphore.signal()
if j == dataSourceArray.count {
print("completion3")
completion(true, dataSourceArrayWithWishes)
}
} else {
dispatchSemaphore.signal()
}
}
dispatchSemaphore.wait()
}
}
} else {
j = j + 1
dispatchSemaphoreOuter.signal()
}
}
}
// without this code the function will not fire a completion if only a single empty list is in dataSourceArray
// if listCounter == dataSourceArray.count {
// print("completion")
// completion(true, dataSourceArrayWithWishes)
// }
// continue
}
}
}
如果 if (querySnapshot?.documents.count)! > 0
是 true
,此方法工作正常,但如果 dataSourceArray
只包含一个空的 list
,它会失败,因为它永远不会触发 completion
.我尝试用 listCounter
和底部的代码修复它。这种 类的 有效但不干净,因为它总是触发 2 completions
这在应用程序中看起来很奇怪。如果 dataSourceArray
?
中只有一个空的 list
,谁知道我该如何解决这个问题
我真的被困在这里了,所以我很高兴得到每一个帮助!
您忘记在 if (querySnapshot?.documents.count)! > 0
的 else
分支中调用完成处理程序
注意:正如其他评论者已经指出的那样,最好将您的代码拆分为多个函数以提高可读性和可维护性。
您可以将完成处理程序作为参数传递给这些函数。
我有一个检索数据的方法。此数据可以但 不必 包含图像。下面的方法是 按顺序 检索数据,这对应用程序至关重要:
static func getWishes(dataSourceArray: [Wishlist], completion: @escaping (_ success: Bool, _ dataArray: [Wishlist]) -> Void){
var dataSourceArrayWithWishes = dataSourceArray
let db = Firestore.firestore()
let userID = Auth.auth().currentUser!.uid
var j = 0
let dispatchSemaphore = DispatchSemaphore(value: 0)
let dispatchSemaphoreOuter = DispatchSemaphore(value: 0)
let dispatchQueue1 = DispatchQueue(label: "taskQueue")
var listCounter = 0
dispatchQueue1.async {
for list in dataSourceArray {
listCounter += 1
db.collection("users").document(userID).collection("wishlists").document(list.name).collection("wünsche").order(by: "wishCounter").getDocuments() { ( querySnapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
// dispatch group to make sure completion only fires when for loop is finished
// append every Wish to array at wishIDX
var i = 0
let dispatchQueue = DispatchQueue(label: "taskQueue1")
if (querySnapshot?.documents.count)! > 0 {
dispatchQueue.async {
for document in querySnapshot!.documents {
let documentData = document.data()
let imageUrlString = document["imageUrl"] as? String ?? ""
if let imageUrl = URL(string: imageUrlString) {
KingfisherManager.shared.retrieveImage(with: imageUrl, options: nil, progressBlock: nil, completionHandler: { result in
var image = UIImage()
switch result {
case .success(let abc):
image = abc.image
case .failure(let error):
print(error)
break
}
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: image))
i = i + 1
if i == querySnapshot?.documents.count {
j = j + 1
dispatchSemaphoreOuter.signal()
dispatchSemaphore.signal()
if j == dataSourceArray.count {
print("completion1")
completion(true, dataSourceArrayWithWishes)
}
} else {
dispatchSemaphore.signal()
}
})
} else {
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: image))
i = i + 1
if i == querySnapshot?.documents.count {
j = j + 1
dispatchSemaphoreOuter.signal()
dispatchSemaphore.signal()
if j == dataSourceArray.count {
print("completion3")
completion(true, dataSourceArrayWithWishes)
}
} else {
dispatchSemaphore.signal()
}
}
dispatchSemaphore.wait()
}
}
} else {
j = j + 1
dispatchSemaphoreOuter.signal()
}
}
}
// without this code the function will not fire a completion if only a single empty list is in dataSourceArray
// if listCounter == dataSourceArray.count {
// print("completion")
// completion(true, dataSourceArrayWithWishes)
// }
// continue
}
}
}
如果 if (querySnapshot?.documents.count)! > 0
是 true
,此方法工作正常,但如果 dataSourceArray
只包含一个空的 list
,它会失败,因为它永远不会触发 completion
.我尝试用 listCounter
和底部的代码修复它。这种 类的 有效但不干净,因为它总是触发 2 completions
这在应用程序中看起来很奇怪。如果 dataSourceArray
?
list
,谁知道我该如何解决这个问题
我真的被困在这里了,所以我很高兴得到每一个帮助!
您忘记在 if (querySnapshot?.documents.count)! > 0
else
分支中调用完成处理程序
注意:正如其他评论者已经指出的那样,最好将您的代码拆分为多个函数以提高可读性和可维护性。
您可以将完成处理程序作为参数传递给这些函数。