【问题标题】:How can we wait for HTTP requests to finish?我们如何等待 HTTP 请求完成?
【发布时间】:2017-07-04 09:26:29
【问题描述】:

使用关于 SO 的几个答案,我们设法编写并执行了一个基本的 HTTP 请求:

import Foundation

let url:URL = URL(string: "http://jsonplaceholder.typicode.com/posts")!
let session = URLSession.shared

var request = URLRequest(url: url)
request.httpMethod = "POST"
let paramString = "data=Hello"
request.httpBody = paramString.data(using: String.Encoding.utf8)

let task = session.dataTask(with: request as URLRequest) {
    (data, response, error) in

    guard let data = data, let _:URLResponse = response, error == nil else {
        print("error")
        return
    }

    let dataString: String =  String(data: data, encoding: String.Encoding.utf8)!
    print("here")

    print("Data: \(dataString)")
    print("Response: \(response!)")
}

task.resume()
while task.response == nil {}
print("Done")

您会注意到,我们已经忙着等待,直到设置了task.response。但是,既不打印数据也不打印响应,只打印here

经过无数次尝试以这种或那种方式包装东西后,我们确定这里有一个 Heisenbug:在代码中什么都不改变,有时会打印 here,有时什么也不打印,而且非常非常少见 dataString(更不用说 @ 987654327@).

所以我们在print("Done") 之前插入sleep(3),然后,奇迹般的,我们得到了所有的打印。

然后我们大叫了一声(我可能真的扔了一些东西),短暂地考虑过完全放弃 Swift,但随后冷静下来,像先生们一样面面相觑,然后在这里发帖。

显然,无论任何异步任务(线程?)是否仍在运行,主线程都会终止,从而杀死所有的子线程。我们如何防止这种情况发生,即“加入”线程?

额外问题: Alamofire 是否在幕后处理这个问题?

【问题讨论】:

标签: swift http asynchronous concurrency


【解决方案1】:

使用 Matt Gallagher 的 CwUtils,我实现了一个简单的 CountdownLatch 来完成这项工作:

import Foundation
import CwlUtils

<...>

let task = session.dataTask(with: request as URLRequest) {
    (data, response, error) in

    <...>
    latch.countDown()
}

task.resume()
latch.await()

【讨论】:

    【解决方案2】:

    最直接(和内置)的方法可能是使用DispatchSemaphore

    <...>
    
    let sem = DispatchSemaphore(value: 0)
    
    let task = session.dataTask(with: request as URLRequest) {
        (data, response, error) in
    
        <...>
        sem.signal()
    }
    
    task.resume()
    sem.wait()
    

    【讨论】:

    • 投反对票的人能否解释一下为什么他们认为这是一个糟糕的解决方案,并在理想情况下提出一个更好的选择?
    • 如果异步任务在sem.signal() 之前向主队列(或任何队列sem.wait() 块)发送内容,则会出现问题;那么我们就陷入了僵局,如here所见。
    【解决方案3】:

    主动等待似乎是 GCD 上的唯一途径。使用标准库材料,这是有效的:

    import Foundation
    
    <...>
    
    var done = false
    
    let task = session.dataTask(with: request as URLRequest) {
        (data, response, error) in
    
        <...>
        done = true
    }
    
    task.resume()
    
    repeat {
        RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1))
    } while !done
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-19
      • 1970-01-01
      • 2019-05-19
      • 1970-01-01
      • 2019-05-08
      • 1970-01-01
      • 2017-08-08
      • 1970-01-01
      相关资源
      最近更新 更多