按顺序执行带有可选异步调用的for循环
Execute for loop with optional asynchronous calls in order
我有一个 function
来检索我正在使用 for-loop
做的一些数据。在其中 asynchronous function
可能被称为 但不一定是 。在 loop
完成后,我也在用 DispatchGroup
触发 completion
。问题是我需要循环执行 in order!
这是我的代码:
// dispatch group to make sure completion only fires when for loop is finished
let group = DispatchGroup()
// append every Wish to array at wishIDX
for document in querySnapshot!.documents {
group.enter()
let documentData = document.data()
let imageUrlString = document["imageUrl"] as? String ?? ""
let wishIDX = documentData["wishlistIDX"] as? Int ?? 0
let imageView = UIImageView()
imageView.image = UIImage()
if let imageUrl = URL(string: imageUrlString) {
let resource = ImageResource(downloadURL: imageUrl)
imageView.kf.setImage(with: resource) { (result) in
switch result {
case .success(_):
print("success")
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: imageView.image!)
group.leave()
case .failure(_):
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: UIImage())
print("fail")
group.leave()
}
}
} else {
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: imageView.image!)
group.leave()
}
}
// for loop is finished -> fire completion
group.notify(queue: DispatchQueue.main) {
completion(true, dataSourceArrayWithWishes)
}
我看到了非常相似的 问题,但我很难将其应用到我的案例中,因为在我的案例中,如果没有 image
。有人可以帮我吗?
需要使用DispatchSemaphore
顺序执行
//MARK: getWishes
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
let group = DispatchGroup()
let dispatchSemaphore = DispatchSemaphore(value: 0)
for list in dataSourceArray {
group.enter()
db.collection("users").document(userID).collection("wishlists").document(list.name).collection("wünsche").order(by: "wishCounter").getDocuments() { ( querySnapshot, error) in
defer {
print("leaving scope:\(String(describing: querySnapshot?.count))")
group.leave()
}
if let error = error {
print(error.localizedDescription)
completion(false, dataSourceArrayWithWishes)
} else {
// dispatch group to make sure completion only fires when for loop is finished
// append every Wish to array at wishIDX
let dispatchQueue = DispatchQueue(label: "taskQueue")
dispatchQueue.async {
for document in querySnapshot!.documents {
group.enter()
let documentData = document.data()
let name = documentData["name"] as? String ?? ""
let link = documentData["link"] as? String ?? ""
let price = documentData["price"] as? String ?? ""
let note = documentData["note"] as? String ?? ""
let imageUrlString = document["imageUrl"] as? String ?? ""
let wishIDX = documentData["wishlistIDX"] as? Int ?? 0
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(name: name, link: link, price: price, note: note, image: image, checkedStatus: false))
print("Signal for next one")
dispatchSemaphore.signal()
group.leave()
})
print("wait for next one")
dispatchSemaphore.wait()
} else {
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(name: name, link: link, price: price, note: note, image: nil, checkedStatus: false))
}
}
}
// for loop is finished -> fire completion
}
}
}
group.notify(queue: DispatchQueue.main) {
print("notify")
completion(true, dataSourceArrayWithWishes)
}
}
不是直接的解决方案,而是让您了解如何处理异步调用的响应以按顺序设置图像。
我为步骤添加了评论。
// filter querySnapshot!.documents. only include the ones that contain an imageUrl
let imagesUrls: [String] = []
// use DispatchGroup to handle completion
let imageDownloadGroup = DispatchGroup()
// use serial DispatchQueue to avoid data race while setting image to data model.
let imageSetQueue = DispatchQueue(label: "com.wishList.imagequeue")
// have a list with a count of valid image urls. images can be either valid or nil
var wishList: [UIImage?] = Array(repeating: nil, count: imagesUrls.count)
// use enumerated list to set image in order.
imagesUrls.enumerated().forEach { (index, item) in
// enter to group
imageDownloadGroup.enter()
// a function to download image
downloadImageFunction { (image) in
imageSetQueue.async {
// set image
wishList[index] = image
// leave from group
imageDownloadGroup.leave()
}
}
}
imageDownloadGroup.notify(queue: .main) {
// handle wishList and call completion
}}
我有一个 function
来检索我正在使用 for-loop
做的一些数据。在其中 asynchronous function
可能被称为 但不一定是 。在 loop
完成后,我也在用 DispatchGroup
触发 completion
。问题是我需要循环执行 in order!
这是我的代码:
// dispatch group to make sure completion only fires when for loop is finished
let group = DispatchGroup()
// append every Wish to array at wishIDX
for document in querySnapshot!.documents {
group.enter()
let documentData = document.data()
let imageUrlString = document["imageUrl"] as? String ?? ""
let wishIDX = documentData["wishlistIDX"] as? Int ?? 0
let imageView = UIImageView()
imageView.image = UIImage()
if let imageUrl = URL(string: imageUrlString) {
let resource = ImageResource(downloadURL: imageUrl)
imageView.kf.setImage(with: resource) { (result) in
switch result {
case .success(_):
print("success")
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: imageView.image!)
group.leave()
case .failure(_):
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: UIImage())
print("fail")
group.leave()
}
}
} else {
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(image: imageView.image!)
group.leave()
}
}
// for loop is finished -> fire completion
group.notify(queue: DispatchQueue.main) {
completion(true, dataSourceArrayWithWishes)
}
我看到了非常相似的 image
。有人可以帮我吗?
需要使用DispatchSemaphore
顺序执行
//MARK: getWishes
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
let group = DispatchGroup()
let dispatchSemaphore = DispatchSemaphore(value: 0)
for list in dataSourceArray {
group.enter()
db.collection("users").document(userID).collection("wishlists").document(list.name).collection("wünsche").order(by: "wishCounter").getDocuments() { ( querySnapshot, error) in
defer {
print("leaving scope:\(String(describing: querySnapshot?.count))")
group.leave()
}
if let error = error {
print(error.localizedDescription)
completion(false, dataSourceArrayWithWishes)
} else {
// dispatch group to make sure completion only fires when for loop is finished
// append every Wish to array at wishIDX
let dispatchQueue = DispatchQueue(label: "taskQueue")
dispatchQueue.async {
for document in querySnapshot!.documents {
group.enter()
let documentData = document.data()
let name = documentData["name"] as? String ?? ""
let link = documentData["link"] as? String ?? ""
let price = documentData["price"] as? String ?? ""
let note = documentData["note"] as? String ?? ""
let imageUrlString = document["imageUrl"] as? String ?? ""
let wishIDX = documentData["wishlistIDX"] as? Int ?? 0
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(name: name, link: link, price: price, note: note, image: image, checkedStatus: false))
print("Signal for next one")
dispatchSemaphore.signal()
group.leave()
})
print("wait for next one")
dispatchSemaphore.wait()
} else {
dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(name: name, link: link, price: price, note: note, image: nil, checkedStatus: false))
}
}
}
// for loop is finished -> fire completion
}
}
}
group.notify(queue: DispatchQueue.main) {
print("notify")
completion(true, dataSourceArrayWithWishes)
}
}
不是直接的解决方案,而是让您了解如何处理异步调用的响应以按顺序设置图像。 我为步骤添加了评论。
// filter querySnapshot!.documents. only include the ones that contain an imageUrl
let imagesUrls: [String] = []
// use DispatchGroup to handle completion
let imageDownloadGroup = DispatchGroup()
// use serial DispatchQueue to avoid data race while setting image to data model.
let imageSetQueue = DispatchQueue(label: "com.wishList.imagequeue")
// have a list with a count of valid image urls. images can be either valid or nil
var wishList: [UIImage?] = Array(repeating: nil, count: imagesUrls.count)
// use enumerated list to set image in order.
imagesUrls.enumerated().forEach { (index, item) in
// enter to group
imageDownloadGroup.enter()
// a function to download image
downloadImageFunction { (image) in
imageSetQueue.async {
// set image
wishList[index] = image
// leave from group
imageDownloadGroup.leave()
}
}
}
imageDownloadGroup.notify(queue: .main) {
// handle wishList and call completion
}}