2025-02-07 10:12:20
,某些文章具有时效性,若有错误或已失效,请在下方留言。You can only call async functions from other async functions, because they might need to suspend themselves and everything that is waiting for them. This leads to a bit of a chicken and egg problem: if only async functions can call other async functions, what starts it all – what calls the very first async function?
您只能从其他异步函数调用 async 函数,因为它们可能需要暂停自身和等待它们的所有内容。这就引出了一个先有鸡还是先有蛋的问题:如果只有异步函数可以调用其他异步函数,那么这一切的起点是什么——什么调用第一个异步函数呢?
Well, there are three main approaches you’ll find yourself using.
好吧,您会发现自己使用三种主要方法。
First, in simple command-line programs using the @main
attribute, you can declare your main()
method to be async. This means your program will immediately launch into an async function, so you can call other async functions freely.
首先,在使用 @main
属性的简单命令行程序中,你可以将 main()
方法声明为 async。这意味着您的程序将立即启动到异步函数中,因此您可以自由调用其他异步函数。
Here’s how that looks in code:
下面是它在代码中的样子:
func processWeather() async {
// Do async work here
}
@main
struct MainApp {
static func main() async {
await processWeather()
}
}
Second, in apps built with something like SwiftUI the framework itself has various places that can trigger an async function. For example, the refreshable()
and task()
modifiers can both call async functions freely.
其次,在使用 SwiftUI 等软件构建的应用程序中,框架本身具有可以触发异步函数的各种位置。例如,refreshable()
和 task()
修饰符都可以自由调用异步函数。
Using the task()
modifier we could write a simple “View Source” app for iOS that fetches the content of a website when our view appears:
使用 task()
修饰符,我们可以为 iOS 编写一个简单的 “View Source” 应用程序,当我们的视图出现时,它会获取网站的内容:
struct ContentView: View {
@State private var sourceCode = ""
var body: some View {
ScrollView {
Text(sourceCode)
}
.task {
await fetchSource()
}
}
func fetchSource() async {
do {
let url = URL(string: "https://apple.com")!
let (data, _) = try await URLSession.shared.data(from: url)
sourceCode = String(decoding: data, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines)
} catch {
sourceCode = "Failed to fetch apple.com"
}
}
}
Tip: When do async work you might end up pushing work away from the main thread where UI updates must happen, but the @State
property wrapper has specifically been written to allow us to modify its value on any thread.
提示: 当执行异步工作时,您最终可能会将工作从必须进行 UI 更新的主线程中推开,但 @State
属性包装器已专门编写,以允许我们在任何线程上修改其值。
The third option is that Swift provides a dedicated Task
API that lets us call async functions from a synchronous function. Now, you might think “wait a minute – how can a synchronous function call an asynchronous function?”
第三种选择是 Swift 提供了一个专用的 Task
API,它允许我们从同步函数中调用异步函数。现在,您可能会想“等一下 – 同步函数如何调用异步函数?
Well, it can’t – at least not directly. Remember, async functions might need to suspend themselves in the future, and synchronous functions don’t know how to do that.
嗯,它不能——至少不能直接。请记住,异步函数将来可能需要暂停自身,而同步函数不知道如何做到这一点。
When you use something like Task
you’re asking Swift to run some async code. If you don’t care about the result you have nothing to wait for – the task will start running immediately while your own function continues, and it will always run to completion even if you don’t store the active task somewhere.
当你使用类似 Task
的东西时,你是在要求 Swift 运行一些异步代码。如果您不关心结果,那么您就没有什么可等待的 – 任务将立即开始运行,而您自己的函数仍在继续,并且即使您没有将活动任务存储在某个位置,它也将始终运行到完成。
This means you’re not awaiting the result of the task, so you won’t run the risk of being suspended. Of course, when you actually want to use any returned value from your task, that’s when await
is required.
这意味着您不会等待任务的结果,因此您不会冒被暂停的风险。当然,当你真的想使用任务中的任何返回值时,那就是需要 await
的时候。
We’ll be looking at Swift’s Task
API in detail later on, but for now we could quickly upgrade our little website source code viewer to work with any URL.
我们稍后会详细研究 Swift 的 Task
API,但现在我们可以快速升级我们的小网站源代码查看器,以处理任何 URL。
This time we’re going to trigger the network fetch using a button press, which is not asynchronous by default, so we’re going to wrap our work in a Task
. This is possible because we don’t need to wait for the task to complete – it will always run to completion as soon as it is made, and will take care of updating the UI for us.
这一次,我们将使用按钮按下来触发网络获取,默认情况下*,它不是*异步的,因此我们将把我们的工作包装在一个 Task
中。这是可能的,因为我们不需要等待任务完成 – 它总是会在任务完成后运行完成,并且会为我们更新 UI。
Here’s how that looks: 这是它的样子:
struct ContentView: View {
@State private var site = "https://"
@State private var sourceCode = ""
var body: some View {
VStack {
HStack {
TextField("Website address", text: $site)
.textFieldStyle(.roundedBorder)
Button("Go") {
Task {
await fetchSource()
}
}
}
.padding()
ScrollView {
Text(sourceCode)
}
}
}
func fetchSource() async {
do {
let url = URL(string: site)!
let (data, _) = try await URLSession.shared.data(from: url)
sourceCode = String(decoding: data, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines)
} catch {
sourceCode = "Failed to fetch \(site)"
}
}
}
It’s remarkable how we can accomplish so much with such little code!
我们怎么能用这么小的代码完成这么多事情,真是了不起!
暂无评论内容