【问题标题】:Using completion handlers inside while loop在 while 循环中使用完成处理程序
【发布时间】:2020-08-27 18:07:32
【问题描述】:

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

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

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

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

它调用我的 API,如果 API 返回一个错误/我没有预料到的值,或者如果 Almofire 由于某种原因无法执行请求,那么它会将一个添加到 tries 变量中。允许的最大尝试次数为 3,由 maxTries 变量给出。如果tries 变量等于maxTries 变量,则布尔型timeout 设置为true。如果tries 变量低于maxTries 变量,则代码将等待timeoutInSeconds - 这是10 秒 - 退出while 循环之前的时间量,这应该再次运行代码。

同样,如果从我的 API 获取数据返回正确的值,则布尔值 found 设置为 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)
        }
    }
}

如有任何问题或需要更多说明,请直接提问。谢谢。

【问题讨论】:

  • 这是因为您的 while 循环在主线程上运行,并且不会等待 getHubCallAPI(在另一个线程上)完成。您可以调用一次 getHubCallAPI,然后在完成时递归调用它(直到 foundtimeout)。
  • @pawello2222 您能否将您的评论添加为我自己和其他人的代码的答案以改进这个问题?
  • 我将其添加为答案。如果我在完成处理程序中误解了您想要的行为,请随时纠正我。

标签: swift while-loop alamofire completionhandler


【解决方案1】:

您可以尝试以下方法:

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?)

【讨论】:

  • 是的,现在更有意义了。谢谢你,没想到它会是太独立的线程。
  • @Nathan 通常当您看到一个词 completion 时,它表示该函数可能是异步的(即在后台运行)。
猜你喜欢
  • 2013-01-03
  • 2018-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-20
  • 2016-06-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多