我一直在测试 Swift 5.5 版本中预览的异步/等待功能,但我无法从异步函数收集结果并使用 SwiftUI 显示它们。这是我的代码:
import SwiftUI
struct AsyncTestView: View {
@State var text: String?
// Async function
func asyncGetText() async -> String {
Thread.sleep(forTimeInterval: 10)
return "My text"
}
// Stores the result of async function
func fetchText() async {
let text = await asyncGetText()
DispatchQueue.main.async {
self.text = text
}
}
var body: some View {
Text(text ?? "Loading")
.onAppear(perform: fetchText)
}
}
这会导致以下错误:
在不支持并发的函数中调用“异步”
将“async”添加到函数“fetchText()”以使其异步
将
async
添加到 fetchText()
函数会导致 .onAppear()
函数出现以下错误:
从“() async -> ()”类型的“异步”函数到同步函数类型“() -> Void”的转换无效
在本文中,他们使用
@asyncHandler
标签来注释 fetchText
函数,但这会导致警告:'@asyncHandler' has been removed from the language'
。
我是您引用的文章的作者。
如发现 SwiftUI 中的并发性中所述,视图可以利用新的
.task { }
和 .refreshable { }
修饰符来异步获取数据。
因此,您现在可以选择以下选项来调用异步代码:
func someSyncMethod() {
doSomeSyncWork()
Task {
await methodThatIsAsync()
}
}
List {
}
.task {
await methodThatIsAsync()
}
List {
}
.refreshable {
await methodThatIsAsync()
}
如果您使用单独的视图模型,请确保将其标记为
@MainActor
以确保在主要参与者上执行属性更新。
我更新了文章的代码:https://github.com/peterfriese/Swift-Async-Await-Experiments
根据 WWDC 会议中的新信息 在 Swift 中认识 async/await WWDC21,23 分 28 秒,现在使用:
完成Task {
someState = await someAsyncFunction()
}
会议截图。
请注意,实例化任务的方法有多种。这是最基本的。您也可以使用
Task.detached
,两种方式都可以获得优先级参数。
检查任务文档和会话
请在 23:05 左右查看探索 Swift WWDC21 中的结构化并发(我推荐整个会议!)以获取更多信息。
我同意@peter-friese的答案,但会为那些阅读本文的人添加从同步桥接到异步时语法的变化:
新语法:
Task {
someState = await someAsyncFunction()
}
替换此语法:
async {
someState = await someAsyncFunction()
}
...并且
Task()
初始化器可以接受优先级参数:
Task(priority: .userInitiated) {
someState = await someAsyncFunction()
}
import SwiftUI
struct AsyncTestView: View {
@State var text = "Loading"
// Async function. Mark nonisolated if you want it on a background thread, e.g. if you have big synchronous loop.
func asyncGetText() async -> String {
try? await Task.sleep(nanoseconds: 3 * NSEC_PER_SEC)
return "My text"
}
var body: some View {
Text(text)
.task {
text = await asyncGetText()
}
}
}
从 SwiftUI View 调用这个简化的
.task { }
版本
var body: some View {
VStack(spacing:.default) {
yourCustomView
}
.padding()
}
.task { await viewModel.onAppear() }
}
在 ViewModel 中
func onAppear() async {
await getStuffFromNetwork()
}