You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 21:21:39