swift async await task

核心区别:同步上下文 vs 异步上下文

// ❌ 错误:在同步函数中调用 async 方法
func syncFunction() {
    let state = iRobotData.shared.state
    await self.mapState(state)  // 编译错误!
}
// 原因:普通函数不支持"等待",没有暂停点

// ✅ 正确:在异步函数中调用 async 方法
func asyncFunction() async {
    let state = iRobotData.shared.state
    await self.mapState(state)  // 可以等待
}

关键await 只能在支持暂停的上下文中使用,而不是任何地方都能用。


什么是 async/await

async/await 是 Swift 的并发编程语法,让异步代码像同步代码一样直观。

例子:取外卖

传统回调方式(闭包):你在家,打电话点外卖,店员说做好了我打给你。你去干别的,电话响了再去拿。

func orderFood(completion: @escaping (Food) -> Void) {
    // 异步做披萨
    makePizza {
        let pizza = Pizza()
        completion(pizza)  // 好了叫你
    }
}

// 使用:回调地狱
orderFood { pizza in
    eat(pizza)
}

async/await 方式:你在家,打电话点外卖,然后原地等待(不干别的),外卖到了继续。

func orderFood() async -> Food {
    // 异步做披萨,但看起来像同步等待
    let pizza = await makePizza()
    return pizza
}

// 使用:像同步代码
let pizza = await orderFood()
eat(pizza)

关键区别

对比 传统回调 async/await
代码写法 嵌套 线性,像同步
错误处理 多处处理 统一 try-catch
可读性

什么是 Task

Task 是在非异步环境启动异步任务的方法。

类比:你想在厨房做菜,但厨房在外面

// 你在卧室(同步环境)
// 想做菜,但菜只能在厨房(异步环境)做

// ❌ 卧室不支持做菜
func bedroom() {
    await cook()  // 卧室不能直接调用厨房方法
}

// ✅ 叫外卖小哥(Task)去厨房做,你继续在卧室做其他事
func bedroom() {
    Task {
        await cook()  // 外卖小哥去厨房做
    }
    // 你继续玩手机
}

Task 的作用:在同步环境中,创建一个异步任务,并立即返回(不等待结果)。


为什么 withObservationTracking 的闭包不支持 await

withObservationTracking 的 apply 闭包设计为同步,因为它需要立即读取值。

// withObservationTracking 的定义
public func withObservationTracking<T>(
    _ apply: () -> T,  // ← 闭包不是 async
    onChange: ...
) -> T

因为它是同步闭包,所以内部不能用 await

解决方案:用 Task 包一层

withObservationTracking {
    let state = iRobotData.shared.state  // 同步读取
    
    // ✅ 创建 Task,在里面异步处理
    Task {
        await self.mapState(state)  // 异步处理
    }
} onChange: { ... }

执行流程

  1. 立即读取 state(同步)
  2. 创建一个 Task(不等待,立即返回)
  3. Task 在后台异步执行 mapState
  4. apply 闭包继续执行其他代码(如果没有,就结束)

对比:同步 vs 异步 vs Task

1. 同步函数中调用同步方法

func syncFunc() {
    let result = syncMethod()  // ✅ 直接调用,等待结果
    print(result)
}

2. 异步函数中调用异步方法

func asyncFunc() async {
    let result = await asyncMethod()  // ✅ 用 await 等待结果
    print(result)
}

3. 异步函数中调用异步方法(不等待结果)

func asyncFunc() async {
    Task {
        await asyncMethod()  // ✅ 不等待,立即返回
    }
    print("立即执行")  // 先打印这个
    // asyncMethod 在后台执行
}

4. 同步函数中调用异步方法

func syncFunc() {
    await asyncMethod()  // ❌ 编译错误!同步函数不能等待
    
    Task {
        await asyncMethod()  // ✅ 创建 Task,不等待
    }
}

你的例子重新说明

错误写法 ❌

withObservationTracking {  // 同步闭包
    let state = iRobotData.shared.state
    await self.mapState(state)  // ❌ 编译错误
    // 原因:同步闭包不能用 await
} onChange: { ... }

正确写法 ✅

withObservationTracking {  // 同步闭包
    let state = iRobotData.shared.state
    Task {  // 创建异步任务
        await self.mapState(state)  // ✅ 在 Task 里可以用 await
    }
    // 闭包立即结束,Task 在后台慢慢执行
} onChange: { ... }

异步函数中调用同步函数

完全可以,而且不需要任何特殊处理。

// 同步函数
func syncMethod() -> String {
    return "同步结果"
}

// 异步函数
func asyncMethod() async -> String {
    // ✅ 直接调用同步函数,不需要 await
    let result = syncMethod()
    return result
}

为什么可以?

调用方向 是否需要 await 原因
异步函数 → 同步函数 ❌ 不需要 同步函数立即返回,不会阻塞,直接调用即可
同步函数 → 异步函数 ✅ 需要 Task 同步函数不能暂停,必须用 Task 包装

图解

┌─────────────────────────────────────────────────────────────┐
│                    异步函数 (async)                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  可以直接调用同步函数 ✅                              │   │
│  │  let a = syncMethod()  ← 立即返回                    │   │
│  │                                                      │   │
│  │  调用异步函数需要用 await ⚠️                          │   │
│  │  let b = await asyncMethod()  ← 可能等待             │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                    同步函数 (非 async)                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  可以直接调用同步函数 ✅                              │   │
│  │  let a = syncMethod()                               │   │
│  │                                                      │   │
│  │  调用异步函数需要用 Task { } ❌                       │   │
│  │  Task { let b = await asyncMethod() }               │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

总结表格

场景 写法 是否需要特殊处理
同步 → 同步 syncMethod() ❌ 不需要
同步 → 异步 Task { await asyncMethod() } ✅ 需要 Task
异步 → 同步 syncMethod() ❌ 不需要
异步 → 异步 await asyncMethod() ✅ 需要 await

一句话记住

异步函数可以"向下"调用同步函数(直接调),同步函数不能"向上"调用异步函数(需要 Task)。

什么时候用 Task

场景 是否用 Task
async 函数中调用 async 方法,想等待结果 ❌ 不用,直接用 await
async 函数中调用 async 方法,不想等待 ✅ 用 Task { }
同步函数中调用 async 方法 ✅ 用 Task { }
在闭包(不支持 async)中调用 async 方法 ✅ 用 Task { }

总结

关键词 含义
async 标记函数是异步的,可能内部会等待
await 等待异步函数完成,只能在 async 函数中使用
Task 在同步环境中启动异步任务,不等待结果

一句话await 就像"暂停等待",只能在支持暂停的地方用;Task 是"派发任务",可以在任何地方用,不等待结果。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容