我们如何等待HTTP请求完成?

时间:2017-02-15 15:55:15

标签: swift http asynchronous concurrency

在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 (更不用说response)。

所以我们在sleep(3)之前插入print("Done"),奇迹奇迹,我们得到所有的印刷品。

然后我们大声喊叫(我实际上可能已经抛出了一些东西),简单地想一想放弃Swift,但后来平静下来,像sirs一样面对并发布在这里。

显然,主线程终止是否有任何异步任务(线程?)仍然在运行,从而终止所有的spawn。我们怎样才能防止这种情况发生,即"加入"线程?

奖金问题: Alamofire是否在幕后处理这个问题?

3 个答案:

答案 0 :(得分:0)

使用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()

答案 1 :(得分:0)

积极等待似乎是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

答案 2 :(得分:-1)

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

<...>

let sem = DispatchSemaphore(value: 0)

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

    <...>
    sem.signal()
}

task.resume()
sem.wait()