如果执行嵌套的异步调用,则函数运行两次,否则运行一次。需要帮助来预先确定何时会发生这种情况

Posted

技术标签:

【中文标题】如果执行嵌套的异步调用,则函数运行两次,否则运行一次。需要帮助来预先确定何时会发生这种情况【英文标题】:Function runs twice if nested async calls are executed and once otherwise. Need help pre-determining when this will happen 【发布时间】:2019-04-11 05:07:48 【问题描述】:

func handleGetAllPhotoURLs 是从下面的行调用的,我已经确认这行代码只执行一次并带有断点。

_ = FlickrClient.getAllPhotoURLs(currentPin: self.currentPin, fetchCount: fetchCount, completion: self.handleGetAllPhotoURLs(pin:urls:error:))

根据我的打印语句的输出,该函数运行两次,因为如果 urls.count 不为零,它会打印两行输出。但是,如果 urls.count 为零,那么我只会得到一个打印语句,说明“urls.count ---> 0”

handleGetAllPhotoURLs ---> urls.count ---> 0 //这行总是打印出来的

handleGetAllPhotoURLs ---> urls.count ---> 21 //仅当urls参数不为空时才打印此行

func handleGetAllPhotoURLs(pin: Pin, urls: [URL], error: Error?)
    print("handleGetAllPhotoURLs ---> urls.count  ---> \(urls.count)")

    let backgroundContext: NSManagedObjectContext! = dataController.backGroundContext
    if let error = error 
        print("func mapView(_ mapView: MKMapView, didSelect... \n\(error)")
        return
    

    let pinId = pin.objectID
    backgroundContext.perform 
        let backgroundPin = backgroundContext.object(with: pinId) as! Pin
        backgroundPin.urlCount = Int32(urls.count)
        try? backgroundContext.save()
    

    for (index, currentURL) in urls.enumerated() 
        URLSession.shared.dataTask(with: currentURL, completionHandler:  (imageData, response, error) in
            guard let imageData = imageData else return
            connectPhotoAndPin(dataController: self.dataController, currentPin:  pin , data: imageData, urlString: currentURL.absoluteString, index: index)
        ).resume()
    

另外,我有一个 UILabel,它只在 urls.count 为零时显示,我只想在 urls 为空时显示它。

现在,如果 urls 不为空,应用程序会非常快速地闪烁空消息 UILabel。现在这对我来说很有意义,因为 print 语句显示 urls 数组暂时为空。

当 urls.count 不为零时,我有没有办法确定避免向用户闪烁空消息 UILabel?

编辑:根据要求在下面添加了代码。调用下面的函数以在完成处理程序中获取 [URL]。然后完成处理程序被送入: func handleGetAllPhotoURLs(pin: Pin, urls: [URL], error: Error?)

class func getAllPhotoURLs(currentPin: Pin, fetchCount count: Int, completion: @escaping (Pin, [URL], Error?)->Void)-> URLSessionTask?
    let latitude = currentPin.latitude
    let longitude = currentPin.longitude
    let pageNumber = currentPin.pageNumber


    let url = Endpoints.photosSearch(latitude, longitude, count, pageNumber).url

    var array_photo_URLs = [URL]()
    var array_photoID_secret = [[String: String]]()
    var array_URLString = [String]()
    var array_URLString2 = [String]()
    var count = 0

    let task = URLSession.shared.dataTask(with: url)  data, response, error in
        guard let dataObject = data, error == nil else 
            DispatchQueue.main.async 
                completion(currentPin, [], error)
            
            return
        

        do 
            let temp = try JSONDecoder().decode(PhotosSearch.self, from: dataObject)
            temp.photos.photo.forEach
                let tempDict = [$0.id : $0.secret]
                array_photoID_secret.append(tempDict)

                let photoURL = FlickrClient.Endpoints.getOnePicture($0.id, $0.secret)
                let photoURLString = photoURL.toString
                array_URLString.append(photoURLString)

                getPhotoURL(photoID: $0.id, secret: $0.secret, completion:  (urlString, error) in
                    guard let urlString = urlString else return
                    array_URLString2.append(urlString)
                    array_photo_URLs.append(URL(string: urlString)!)
                    count = count + 1
                    if count == temp.photos.photo.count 
                        completion(currentPin, array_photo_URLs, nil)
                    
                )
            
            completion(currentPin, [], nil)
            return
         catch let conversionErr 
            DispatchQueue.main.async 
                completion(currentPin, [], conversionErr)
            
            return
        
    
    task.resume()
    return task

【问题讨论】:

函数的内容不影响函数被调用的次数。我们必须看看你是如何调用那个方法的。 嗨。进行了编辑,希望能提供更多信息。 class func getAllPhotoURLs() 返回一个完成处理程序,然后进入 func handleGetAllPhotoURLs()。如果还有什么我可以提供的帮助,请告诉我 【参考方案1】:

do 块中,您调用了两次completion。请看更正,

do 
    let temp = try JSONDecoder().decode(PhotosSearch.self, from: dataObject)
    if temp.photos.photo.isEmpty == false 
       temp.photos.photo.forEach
        let tempDict = [$0.id : $0.secret]
        array_photoID_secret.append(tempDict)

        let photoURL = FlickrClient.Endpoints.getOnePicture($0.id, $0.secret)
        let photoURLString = photoURL.toString
        array_URLString.append(photoURLString)

        getPhotoURL(photoID: $0.id, secret: $0.secret, completion:  (urlString, error) in
            guard let urlString = urlString else return
            array_URLString2.append(urlString)
            array_photo_URLs.append(URL(string: urlString)!)
            count = count + 1
            if count == temp.photos.photo.count 
                completion(currentPin, array_photo_URLs, nil)
            
        )
      
     else 
        completion(currentPin, [], nil)
    
    return

【讨论】:

非常感谢。我找错地方了。没有意识到我两次返回完成处理程序。

以上是关于如果执行嵌套的异步调用,则函数运行两次,否则运行一次。需要帮助来预先确定何时会发生这种情况的主要内容,如果未能解决你的问题,请参考以下文章

执行函数取决于哪个异步调用首先返回

Cloud Code 函数运行两次

python中为啥我的for循环里嵌套的if只能循环一次?

十异步

C# 异步方法加await调用不就变成同步方法了吗

Lambda 异步调用