在while循环中使用完成处理程序

问题描述

如何在while循环中使用完成处理程序/调度队列?

我有一个称为getHub()方法,它是一个完成处理程序,因为我希望在完成相关值后执行代码。当用户按下按钮时,我称之为:

SetupAPI().getHub(completion: { response,error in
     print("testing")
     print(response)
     print(error)
})

(上面的代码是下面所有代码的结尾)

调用了我的API,如果该API返回了我所期望的错误/值,或者Almofire由于某种原因无法执行该请求,则会在tries变量中添加一个。允许的最大尝试次数maxTries变量给定的3次。如果tries变量等于maxTries变量,则将布尔timeout设置为true。如果tries变量低于maxTries变量,则代码将等待timeoutInSeconds(这是10秒)的时间,然后退出while循环,这将再次运行代码。 / p>

类似地,如果从我的API中获取数据后返回了正确的值,则将found设置为true

如果这些变量之一为true,则while循环中断。并将错误发送回上述代码的完成处理程序(然后,我可以告诉用户某些地方出了问题)。

但是,当我运行它时,上面的完成处理程序还没有完成,并且代码只运行了while循环,并在控制台填充startingfetching时一遍又一遍地调用函数。通过我的两个打印语句在下面的代码中进行调试。有什么问题,在这种情况下可以使用dispatchQueue /完成处理程序吗?

通过上述代码调用函数

func getHub(completion: @escaping (Bool,Error?) -> Void) {
    var tries = 0
    let maxTries = 3
    let timeoutInSeconds = 10.0
    var found = false
    var timeout = false
    
    while !found || !timeout{
        print("starting")
        getHubCallAPI(completion: {status,error in
            if(error == nil){
                print(status)
                if (status == "No documents found"){
                    if(tries >= maxTries){
                        print("Tired too many times")
                        timeout = true
                        return completion(false,nil)
                    }
                    tries += 1
                    dispatchQueue.main.asyncAfter(deadline: .Now() + timeoutInSeconds){
                        return
                    }
                }else{
                    found = true
                    print("Hub found")
                    return completion(true,nil)
                }
            }else{
                print("error")
                return completion(false,error)
            }
        })
    }
}

调用API并将其返回到^^以上函数函数

func getHubCallAPI(completion: @escaping (String,Error?) -> Void) {
    print("fetching")
    AF.request("https://discovery.ellisn.com",encoding: URLEncoding.default).response { response in
        print("Request: \(response.request)")
        print("Response: \(response.response)")
        print("Error: \(response.error)")
        if(response.error != nil){
            return completion("",response.error)
        }
        if let data = response.data,let status = String(data: data,encoding: .utf8) {
            return completion(status,nil)
        }
    }
}

任何问题,或者需要进一步澄清,然后问。谢谢。

解决方法

您可以尝试以下操作:

func getHub(triesLeft: Int = 3,completion: @escaping (Bool,Error?) -> Void) {
    let timeoutInSeconds = 1.0

    print("starting")
    getHubCallAPI(completion: { status,error in
        if error == nil {
            print(status)
            if status != "No documents found" {
                print("Hub found")
                return completion(true,nil)
            }
        } else {
            print("error")
            return completion(false,error) // comment out if the loop should continue on error
        }
        if triesLeft <= 1 {
            print("Tried too many times")
            return completion(false,nil)
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + timeoutInSeconds) {
            getHub(triesLeft: triesLeft - 1,completion: completion)
        }
    })
}

就这样叫一次:

getHub(triesLeft: 2,completion: { ... })

请注意,除非出于其他原因需要,否则无需返回(Bool,Error?)。第二个参数始终为nil-您可能希望传播错误。从理论上讲,您可以返回(String?,Error?)