【问题标题】:Execute for loop with optional asynchronous calls in order按顺序执行带有可选异步调用的 for 循环
【发布时间】:2020-07-18 13:54:46
【问题描述】:

我有一个function 来检索我正在使用for-loop 进行的一些数据。在里面,asynchronous function 可能被称为但不一定。在loop 完成后,我也在触发completion,我正在使用DispatchGroup。问题是我需要循环执行顺序

这是我的代码:

// 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)
}

我看到this 的问题非常相似,但我正在努力将其应用到我的案例中,因为在我的案例中,如果没有image,我可能不会做出asynchronous call。有谁能帮帮我吗?

【问题讨论】:

    标签: ios swift asynchronous completionhandler dispatchgroup


    【解决方案1】:

    需要使用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)
        }
    }
    

    【讨论】:

    • 感谢您的回答,但它现在根本没有加载。也没有显示任何错误,所以我猜它卡在某个地方
    • 你可以调试它...发生了什么...让我再次查看代码...
    • 以前从未使用过Semaphore,所以我不太清楚如何调试它
    • 加上我的调试技能被降低到print语句:D
    • let dispatchSemaphore = DispatchSemaphore(value: 1) 在这里放 1 而不是 0
    【解决方案2】:

    不是直接的解决方案,而是让您了解如何处理异步调用的响应以按顺序设置图像。 我为这些步骤添加了 cmets。

    // 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
    }}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多