如何处理 Swift 中的异步请求?

How do I handle async requests in Swift?

我在我的一个函数中进行了网络服务调用。我在如何构建它时遇到问题,因此逻辑是干净的而不是多余的。这是我拥有的:

public func getTimes() -> [TimeName: MyResult] {
    let deferredTask: () -> [TimeName: MyResult] = {
        var computed = self.computeTimes()
        // Do something
        return computed
    }

    // Calculate timezone
    if let tz = timezone {
        timeZone = tz
        return deferredTask()
    } else {
        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) {
            (data, response, error) in

            // Do something with data

            return deferredTask() // Getting a compiler error here btw: is not convertible to 'Void'
        }.resume()
    }
}

然后我使用 MyInstance.getTimes() 调用它。不过感觉有些不对劲。设置 deferredTask 变量是避免冗余代码的好方法吗?

此外,当我从另一个函数调用它时,我会使用 var test = MyInstance.getTimes()。但这意味着 test 处于异步状态,在 getTimes.

中完成 Web 服务之前,我无法在我的其他函数中使用它

在 Swift 中处理此问题的好方法是什么?

正如您所说,您不能这样做:

public func getTimes() -> [TimeName: MyResult] {
    let deferredTask: () -> [TimeName: MyResult] = {
        var computed = self.computeTimes()
        // Do something
        return computed
    }
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) {
            (data, response, error) in
            // Do something with data
            return deferredTask()
     }.resume()
}

正如我认为您正试图说的那样,问题是 return deferredTask() 是 return 来自周围闭包的 - 而不是来自 getTimes。您不能异步 return 来自函数调用的值!这就是异步的本质。

一个典型的标准解决方案是编写getTimes本身来接受一个回调函数作为它的参数。它本身 return 没什么。异步方法完成后,它调用回调,从而在将来某个时间将信息返回给原始调用者。

为了向您展示我的意思,我将从故事中完全删除您的 deferredTask,只专注于异步任务和回调。所以我们会有这个:

public func getTimes(f:(Any) -> Void) -> () {
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) {
            (data, response, error) in
            // ...
            f(whatever)
     }.resume()
}

这个架构的要点是对 f(whatever) 的调用有效地返回到首先交给我们 f 的人 - 特别是如果那个实体把 self 放到某个地方.