Swift 中的异步线程 - 如何处理?
Asynchronous thread in Swift - How to handle?
我正在尝试从 URL 恢复数据集(在通过 parseJSON 函数解析 JSON 之后正确 - 我没有在下面的代码片段中附加它。
结果 returns nil - 我相信这是因为 retrieveData 函数中的闭包是异步处理的。我无法将结果保存到 targetData.
在此先感谢您的帮助。
class MyClass {
var targetData:Download?
func triggerEvaluation() {
retrieveData(url: "myurl.com") { downloadedData in
self.targetData = downloadedData
}
print(targetData) // <---- Here is where I get "nil"!
}
func retrieveData(url: String, completion: @escaping (Download) -> ()) {
let myURL = URL(url)!
let mySession = URLSession(configuration: .default)
let task = mySession.dataTask(with: myURL) { [self] (data, response, error) in
if error == nil {
if let fetchedData = data {
let safeData = parseJSON(data: fetchedData)
completion(safeData)
}
} else {
//
}
}
task.resume()
}
}
是的,它是 nil
,因为 retrieveData
是异步运行的,即在您点击 print
语句时数据尚未检索。将 print
语句(可能还有 UI 的所有更新)移动到闭包中,就在您设置 self.targetData
) 的位置。
例如
func retrieveData(from urlString: String, completion: @escaping (Result<Download, Error>) -> Void) {
let url = URL(urlString)!
let mySession = URLSession.shared
let task = mySession.dataTask(with: url) { [self] data, response, error in
guard
let responseData = data,
error == nil,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
DispatchQueue.main.async {
completion(.failure(error ?? NetworkError.unknown(response, data))
}
return
}
let safeData = parseJSON(data: responseData)
DispatchQueue.main.async {
completion(.success(safeData))
}
}
task.resume()
}
在哪里
enum NetworkError: Error {
case unknown(URLResponse?, Data?)
}
那么调用者会:
func triggerEvaluation() {
retrieveData(from: "https://myurl.com") { result in
switch result {
case .failure(let error):
print(error)
// handle error here
case .success(let download):
self.targetData = download
// update the UI here
print(download)
}
}
// but not here
}
一些不相关的观察:
您不想为每个请求都创建一个新的 URLSession
。只创建一个并将其用于所有请求,或者像我上面那样使用 shared
。
确保 retrieveData
中的每个执行路径都调用闭包。它可能还不是很关键,但是当我们编写异步代码时,我们总是希望确保调用闭包。
为了检测错误,我建议使用 Result
模式,如上所示,它是 .success
或 .failure
,但无论哪种方式你都知道将调用闭包。
确保模型更新和 UI 更新发生在主队列上。通常,我们会让 retrieveData
将对闭包的调用分派到主队列,这样调用者就不会受到影响。 (例如,这就是像 Alamofire 这样的图书馆所做的。)
我正在尝试从 URL 恢复数据集(在通过 parseJSON 函数解析 JSON 之后正确 - 我没有在下面的代码片段中附加它。
结果 returns nil - 我相信这是因为 retrieveData 函数中的闭包是异步处理的。我无法将结果保存到 targetData.
在此先感谢您的帮助。
class MyClass {
var targetData:Download?
func triggerEvaluation() {
retrieveData(url: "myurl.com") { downloadedData in
self.targetData = downloadedData
}
print(targetData) // <---- Here is where I get "nil"!
}
func retrieveData(url: String, completion: @escaping (Download) -> ()) {
let myURL = URL(url)!
let mySession = URLSession(configuration: .default)
let task = mySession.dataTask(with: myURL) { [self] (data, response, error) in
if error == nil {
if let fetchedData = data {
let safeData = parseJSON(data: fetchedData)
completion(safeData)
}
} else {
//
}
}
task.resume()
}
}
是的,它是 nil
,因为 retrieveData
是异步运行的,即在您点击 print
语句时数据尚未检索。将 print
语句(可能还有 UI 的所有更新)移动到闭包中,就在您设置 self.targetData
) 的位置。
例如
func retrieveData(from urlString: String, completion: @escaping (Result<Download, Error>) -> Void) {
let url = URL(urlString)!
let mySession = URLSession.shared
let task = mySession.dataTask(with: url) { [self] data, response, error in
guard
let responseData = data,
error == nil,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
DispatchQueue.main.async {
completion(.failure(error ?? NetworkError.unknown(response, data))
}
return
}
let safeData = parseJSON(data: responseData)
DispatchQueue.main.async {
completion(.success(safeData))
}
}
task.resume()
}
在哪里
enum NetworkError: Error {
case unknown(URLResponse?, Data?)
}
那么调用者会:
func triggerEvaluation() {
retrieveData(from: "https://myurl.com") { result in
switch result {
case .failure(let error):
print(error)
// handle error here
case .success(let download):
self.targetData = download
// update the UI here
print(download)
}
}
// but not here
}
一些不相关的观察:
您不想为每个请求都创建一个新的
URLSession
。只创建一个并将其用于所有请求,或者像我上面那样使用shared
。确保
retrieveData
中的每个执行路径都调用闭包。它可能还不是很关键,但是当我们编写异步代码时,我们总是希望确保调用闭包。为了检测错误,我建议使用
Result
模式,如上所示,它是.success
或.failure
,但无论哪种方式你都知道将调用闭包。确保模型更新和 UI 更新发生在主队列上。通常,我们会让
retrieveData
将对闭包的调用分派到主队列,这样调用者就不会受到影响。 (例如,这就是像 Alamofire 这样的图书馆所做的。)