在完成块完成之前返回的函数

Posted

技术标签:

【中文标题】在完成块完成之前返回的函数【英文标题】:Functions returning before completion block finished 【发布时间】:2017-09-20 14:15:28 【问题描述】:

我在使用 Swifts GCD 时遇到问题。在将下载任务添加到数组时,我有一个函数可以在实际继续下载图像本身之前检查以确保令牌有效。

问题是如果令牌无效,则在获取新令牌后,preloadImage 代码运行速度太快,以至于 requestPreloadJWTToken 完成块无法设置 downloadTask 变量,因此它不是 nil。

下面是下载任务的设置。

static func preloadImage(sku: String)  
    // If the token exists AND is VALID (not expired) - Get the image.

    let group = DispatchGroup()
    var downloadTask : RetrieveImageDownloadTask? = nil

    if ImageManager.tokenExists() && ImageManager.tokenIsNotExpired()

        let imageURL = ImageManager.URLBuilder(sku: sku)

        // Create the request and add the token string to the authorization header
        var urlRequest = try! URLRequest(url: URL(string: imageURL)!)
        urlRequest.setValue("Bearer \(ImageManager.getTokenString()!)", forHTTPHeaderField: "Authorization")
        downloadTask = NetRequest.downloadImage(urlRequest: urlRequest, sku: sku)
    
    // Else if the token exists AND is INVALID (expired) - Delete the old token, get a new one, and fetch the image
    else if ImageManager.tokenExists() && !ImageManager.tokenIsNotExpired()
        print("Token expired... Getting new token.")


        _ = ImageManager.deleteToken()
        if let tokenRequest = NetRequest.newTokenRequest(url: "http://\(UrlHelper.buildUrlFrom(SettingsManager.serverURL))
            group.enter()
            tokenRequest.requestPreloadJWTToken()
                let imageURL = ImageManager.URLBuilder(sku: sku)

                // Create the request and add the token string to the authorization header
                var urlRequest = try! URLRequest(url: URL(string: imageURL)!)
                urlRequest.setValue("Bearer \(ImageManager.getTokenString()!)", forHTTPHeaderField: "Authorization")

                print("Token renewed. Fetching image...")
                downloadTask = NetRequest.downloadImage(urlRequest: urlRequest, sku: sku)
            
            group.leave()
        
    
    // If the token doesn't exist, request a new one and fetch the image.
    else
        print("Requesting new token...")

        if let tokenRequest = NetRequest.newTokenRequest(url: "http://\(UrlHelper.buildUrlFrom(SettingsManager.serverURL))
            group.enter()
            tokenRequest.requestPreloadJWTToken() 
                print("Token aquired. Fetching image...")
                let imageURL = ImageManager.URLBuilder(sku: sku)

                // Create the request and add the token string to the authorization header
                var urlRequest = try! URLRequest(url: URL(string: imageURL)!)
                urlRequest.setValue("Bearer \(ImageManager.getTokenString()!)", forHTTPHeaderField: "Authorization")

                downloadTask = NetRequest.downloadImage(urlRequest: urlRequest, sku: sku)
            
            group.leave()
        
    
    // This should always happen
    group.notify(queue: DispatchQueue.main) 
       MainController.imageDownloadTasks.append(downloadTask!)
    

下面是正在创建的令牌请求。

func requestPreloadJWTToken(completionHandler: @escaping () -> (/*RetrieveImageDownloadTask*/))

    // Get the server name, username, and password
    let username = SettingsManager.username
    let password = SettingsManager.password

    let queue = DispatchQueue(label: "Token-Request", qos: .utility, attributes: [.concurrent])

    // Get download directory
    let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory, in: .userDomainMask)

    // Create the header to be used - Add username and password
    self.makeURLRequest()
    self.addPreAuthentication(username, password)

    // Make the request for the token
    Alamofire.download("\(UrlHelper.buildUrlFrom(SettingsManager.serverURL)), method: .post, parameters: nil, headers: self.request.allHTTPHeaderFields, to: destination)
        // Alamofires built in authentication will provide credentials when challenged for authentication
        .authenticate(user: username, password: password)
        .responseString(
            queue: queue,
            completionHandler:  response in

            DispatchQueue.main.async 
                    switch response.result 
                    // If the result was successful, the token string is saved to a file
                    case .success:
                        // Uncomment to see token string value
                        //debugPrint("Value \(response.value!)")
                        completionHandler()
                    case .failure(let error):
                        debugPrint("Failed \(error)")
                    
            
        
    )

如果有人对我如何从 requestPreloadJWT 获取完成块以在 preloadImage 之前完成有所了解,那就太棒了。谢谢!

【问题讨论】:

【参考方案1】:

好的。所以我在发完这篇文章后立即想通了……当然……

无论如何。我只是将 group.leave() inside 卡在我的 preloadImage 方法中,所以它看起来像:

static func preloadImage(sku: String)  
    // If the token exists AND is VALID (not expired) - Get the image.

    let group = DispatchGroup()
    var downloadTask : RetrieveImageDownloadTask? = nil

    if ImageManager.tokenExists() && ImageManager.tokenIsNotExpired()

        let imageURL = ImageManager.URLBuilder(sku: sku)

        // Create the request and add the token string to the authorization header
        var urlRequest = try! URLRequest(url: URL(string: imageURL)!)
        urlRequest.setValue("Bearer \(ImageManager.getTokenString()!)", forHTTPHeaderField: "Authorization")
        downloadTask = NetRequest.downloadImage(urlRequest: urlRequest, sku: sku)
    
    // Else if the token exists AND is INVALID (expired) - Delete the old token, get a new one, and fetch the image
    else if ImageManager.tokenExists() && !ImageManager.tokenIsNotExpired()
        print("Token expired... Getting new token.")


        _ = ImageManager.deleteToken()
        if let tokenRequest = NetRequest.newTokenRequest(url: "http://\(UrlHelper.buildUrlFrom(SettingsManager.serverURL))")
            group.enter()
            tokenRequest.requestPreloadJWTToken()
                let imageURL = ImageManager.URLBuilder(sku: sku)

                // Create the request and add the token string to the authorization header
                var urlRequest = try! URLRequest(url: URL(string: imageURL)!)
                urlRequest.setValue("Bearer \(ImageManager.getTokenString()!)", forHTTPHeaderField: "Authorization")

                print("Token renewed. Fetching image...")
                downloadTask = NetRequest.downloadImage(urlRequest: urlRequest, sku: sku)
                group.leave()
            
        
    
    // If the token doesn't exist, request a new one and fetch the image.
    else
        print("Requesting new token...")

        if let tokenRequest = NetRequest.newTokenRequest(url: "http://\(UrlHelper.buildUrlFrom(SettingsManager.serverURL))")
            group.enter()
            tokenRequest.requestPreloadJWTToken() 
                print("Token aquired. Fetching image...")
                let imageURL = ImageManager.URLBuilder(sku: sku)

                // Create the request and add the token string to the authorization header
                var urlRequest = try! URLRequest(url: URL(string: imageURL)!)
                urlRequest.setValue("Bearer \(ImageManager.getTokenString()!)", forHTTPHeaderField: "Authorization")

                downloadTask = NetRequest.downloadImage(urlRequest: urlRequest, sku: sku)
                group.leave()
            
        
    
    // This should always happen
    group.notify(queue: DispatchQueue.main) 
       MainController.imageDownloadTasks.append(downloadTask!)
    

我希望这可以帮助遇到它的人!

【讨论】:

正在研究我对此的回答。我是否建议也重构您的代码,因为您在每个完成块中都做了几乎相同的事情,所以将所有这些放在一个函数中可能会很有用。 谢谢老兄。是的,我的这部分代码肯定需要重构。对这个领域还是有点陌生​​:p

以上是关于在完成块完成之前返回的函数的主要内容,如果未能解决你的问题,请参考以下文章

完成处理程序在运行另一个任务之前完成任务

函数在继续循环之前不等待条件完成

在完成块 Swift 中返回

在返回函数的变量之前,如何等待 promise 完成?

使用完成块,而不是返回一个变量 - iOS Swift

当模型函数在达到回滚或完成之前返回时,插入的记录会发生啥?