Swift 并发 - 如何从不工作的任务中获取结果
Swift Concurrency - How to get a Result from a task not working
我试图将此示例放入一个简单的项目中 + ViewController 但无法编译
https://www.hackingwithswift.com/quick-start/concurrency/how-to-get-a-result-from-a-task
我正在尝试通过点击按钮从 IBAction 调用 fetchQuotes()
,但是因为 fetchQuotes
被标记为 async
,我收到了一个错误。
我知道我可以将对 fetchQuotes()
的调用包装在一个任务中:
Task {
fetchQuotes()
}
,但这对我来说没有意义,因为 fetchQuotes
已经在创建任务。
有人可以指点一下吗?
这是我的代码:
// https://www.hackingwithswift.com/quick-start/concurrency/how-to-get-a-result-from-a-task
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func buttonTap(_ sender: Any) {
fetchQuotes()
}
func fetchQuotes() async {
let downloadTask = Task { () -> String in
let url = URL(string: "https://hws.dev/quotes.txt")!
let data: Data
do {
(data, _) = try await URLSession.shared.data(from: url)
} catch {
throw LoadError.fetchFailed
}
if let string = String(data: data, encoding: .utf8) {
return string
} else {
throw LoadError.decodeFailed
}
}
let result = await downloadTask.result
do {
let string = try result.get()
print(string)
} catch LoadError.fetchFailed {
print("Unable to fetch the quotes.")
} catch LoadError.decodeFailed {
print("Unable to convert quotes to text.")
} catch {
print("Unknown error.")
}
}
}
enum LoadError: Error {
case fetchFailed, decodeFailed
}
这是我的代码截图 + Xcode 错误:
but this doesn't make sense to me since fetchQuotes is already creating tasks
无关;你已经内化了一个错误的规则,或者没有内化真正的规则。这与谁在“创建任务”无关。它与真正的规则有关,Swift的async/await
最基本的规则是:
您只能从 async
上下文中调用 async
方法。
嗯,方法 buttonTap
不是 async
方法,因此您对 fetchQuotes
的调用不是在 async
上下文中进行的。这是非法的,编译器会阻止你。
但是,如果您将对 fetchQuotes
的调用包装在任务初始值设定项中,则 是 一个 async
上下文。成功!
基本上,您看到的效果就是我所说的厄运倒退。由于对 async
方法的每次调用都必须在 async
上下文中,但由于 您的 来自 Cocoa 的事件,例如 IBAction,是 不是 async
,你的代码怎么可能调用async
方法?答案是任务初始化程序:它为您提供了一个开始的地方。
this doesn't make sense to me since fetchQuotes is already creating tasks
fetchQuotes
内部做的是一个实现细节,它还不如不显式创建任何任务,甚至不进行异步调用。
现在,方法声明的 async
部分告诉编译器该方法可能会挂起调用者线程,因此任何调用者在调用该方法时都需要提供异步上下文。请注意,我说的是 可能暂停 而不是 将暂停 ,这是一个微妙但重要的区别。
该方法创建的任务仅在方法执行期间有效,因此如果您需要能够从非异步上下文中调用该方法,则必须创建新任务。
但是,您可以通过提供非异步重载来整理代码:
func fetchQuotes() {
Task {
await fetchQuotes()
}
}
,可以从您的 @IBAction
调用
@IBAction func buttonTap(_ sender: Any) {
fetchQuotes()
}
无法逃避这一点,如果您希望能够从非异步上下文中调用异步函数,那么您将需要一个 Task
,无论调用的方法是如何实现的.结构化并发任务不可重用。
我试图将此示例放入一个简单的项目中 + ViewController 但无法编译 https://www.hackingwithswift.com/quick-start/concurrency/how-to-get-a-result-from-a-task
我正在尝试通过点击按钮从 IBAction 调用 fetchQuotes()
,但是因为 fetchQuotes
被标记为 async
,我收到了一个错误。
我知道我可以将对 fetchQuotes()
的调用包装在一个任务中:
Task {
fetchQuotes()
}
,但这对我来说没有意义,因为 fetchQuotes
已经在创建任务。
有人可以指点一下吗?
这是我的代码:
// https://www.hackingwithswift.com/quick-start/concurrency/how-to-get-a-result-from-a-task
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func buttonTap(_ sender: Any) {
fetchQuotes()
}
func fetchQuotes() async {
let downloadTask = Task { () -> String in
let url = URL(string: "https://hws.dev/quotes.txt")!
let data: Data
do {
(data, _) = try await URLSession.shared.data(from: url)
} catch {
throw LoadError.fetchFailed
}
if let string = String(data: data, encoding: .utf8) {
return string
} else {
throw LoadError.decodeFailed
}
}
let result = await downloadTask.result
do {
let string = try result.get()
print(string)
} catch LoadError.fetchFailed {
print("Unable to fetch the quotes.")
} catch LoadError.decodeFailed {
print("Unable to convert quotes to text.")
} catch {
print("Unknown error.")
}
}
}
enum LoadError: Error {
case fetchFailed, decodeFailed
}
这是我的代码截图 + Xcode 错误:
but this doesn't make sense to me since fetchQuotes is already creating tasks
无关;你已经内化了一个错误的规则,或者没有内化真正的规则。这与谁在“创建任务”无关。它与真正的规则有关,Swift的async/await
最基本的规则是:
您只能从 async
上下文中调用 async
方法。
嗯,方法 buttonTap
不是 async
方法,因此您对 fetchQuotes
的调用不是在 async
上下文中进行的。这是非法的,编译器会阻止你。
但是,如果您将对 fetchQuotes
的调用包装在任务初始值设定项中,则 是 一个 async
上下文。成功!
基本上,您看到的效果就是我所说的厄运倒退。由于对 async
方法的每次调用都必须在 async
上下文中,但由于 您的 来自 Cocoa 的事件,例如 IBAction,是 不是 async
,你的代码怎么可能调用async
方法?答案是任务初始化程序:它为您提供了一个开始的地方。
this doesn't make sense to me since fetchQuotes is already creating tasks
fetchQuotes
内部做的是一个实现细节,它还不如不显式创建任何任务,甚至不进行异步调用。
现在,方法声明的 async
部分告诉编译器该方法可能会挂起调用者线程,因此任何调用者在调用该方法时都需要提供异步上下文。请注意,我说的是 可能暂停 而不是 将暂停 ,这是一个微妙但重要的区别。
该方法创建的任务仅在方法执行期间有效,因此如果您需要能够从非异步上下文中调用该方法,则必须创建新任务。
但是,您可以通过提供非异步重载来整理代码:
func fetchQuotes() {
Task {
await fetchQuotes()
}
}
,可以从您的 @IBAction
@IBAction func buttonTap(_ sender: Any) {
fetchQuotes()
}
无法逃避这一点,如果您希望能够从非异步上下文中调用异步函数,那么您将需要一个 Task
,无论调用的方法是如何实现的.结构化并发任务不可重用。