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

Using completion handlers inside while loop

如何在 while 循环中使用完成 handlers/dispatchqueue?

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

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

(上面的代码是下面所有代码应该结束的地方)

它调用我的 API,如果 API returns 一个我没有预料到的 error/a 值,或者如果 Almofire 无法执行请求出于某种原因,它会在 tries 变量上加一。允许的最大尝试次数为 maxTries 变量给出的 3 次。如果 tries 变量等于 maxTries 变量,则 bool timeout 设置为 true。如果 tries 变量低于 maxTries 变量,则代码等待 timeoutInSeconds - 即 10 秒 - 退出 while 循环之前的时间量,这应该 运行 代码再一次。

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

如果这些变量中的任何一个为真,则 while 循环中断。并将错误发送回上述代码的完成处理程序(然后允许我告诉用户出了点问题)。

然而,当我 运行 它时,上面的完成处理程序没有完成,代码只是 运行s 通过 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 和 returns 它回到上面的函数 ^^:

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: { ... })

请注意,除非您出于其他原因需要它,否则没有必要 return (Bool, Error?)。第二个参数总是 nil - 你可能想要传播你的错误。理论上你可以 return (String?, Error?).