Swift async函数await未生效 执行顺序异常问题排查
Swift async/await 调用未等待、执行顺序异常问题
问题表现
调用vm.loadData()时期望通过await等待加载完成,日志按1 → 2 → 3顺序输出,实际输出顺序为1 → 3 → 2,写await vm.loadData()的位置抛出编译器警告:No 'async' operations occur within 'await' expression,即使将调用包裹在Task中也未达到预期效果。
相关原代码:
View层原代码
struct Button: View { let vm = ViewModel() var body: some View { Circle().onTapGesture { doSomething } } func doSomething() { print("1") Task { // 警告: No 'async' operations occur within 'await' expression await vm.loadData() } print("3") } }
ViewModel层原代码
class ViewModel: ObservableObject { @Published var data: [String] = [] func loadData() { Task { do { let dataArray = try await DataApi.loadData() if !dataArray.isEmpty { self.data = dataArraay } print("2") } catch { // 错误处理逻辑 } } } }
API层原代码
class DataApi { static func loadData() async throws -> [String] { // 发起网络请求 do { // 数据解码 return data } catch { throw ResponseError.decodingError("\(error)") } return [] } }
根本原因
问题出在ViewModel的loadData()方法定义和调用逻辑两处:
loadData()本身是同步函数:你在方法内部手动创建了一个独立Task执行异步网络请求,loadData()会在Task创建完成后立刻返回,不会等待内部网络请求执行结束。对同步函数加await没有任何等待效果,编译器检测到这一点就会抛出对应的警告。print("3")位置错误:print("3")写在Task闭包外部,属于同步执行逻辑,Task启动后会立刻执行下一行的print("3"),哪怕loadData()是正确的异步方法,也无法保证3在2之后打印。- 额外笔误:ViewModel中赋值时写的
dataArraay存在拼写错误,会导致编译失败。
修复方案
- 将
ViewModel的loadData()标记为async throws方法,直接在方法体内写异步逻辑,不要在方法内部额外套一层独立Task - 将需要在异步操作完成后执行的逻辑(也就是
print("3"))移到Task内部、await语句之后 - 修正拼写错误和闭包调用写法
修复后的ViewModel代码
class ViewModel: ObservableObject { @Published var data: [String] = [] func loadData() async throws { let dataArray = try await DataApi.loadData() if !dataArray.isEmpty { self.data = dataArray } print("2") } }
修复后的View层代码
struct Button: View { let vm = ViewModel() var body: some View { Circle().onTapGesture { doSomething() } } func doSomething() { print("1") Task { try await vm.loadData() print("3") } } }
API层代码逻辑正确,不需要修改。修复后日志将严格按照1 → 2 → 3的顺序输出,编译器警告也会消失。
不要在同步函数内部新建
Task包裹异步逻辑后就把该函数当异步方法使用,这种写法是"发后即忘"的执行逻辑,调用方无法感知异步任务的完成时机,也无法捕获任务抛出的错误。
内容的提问来源于stack exchange,提问作者kissues
相关产品推荐
相关产品推荐

