LESSON 11 / 577 分钟

async/await、Task 与 Actor

异步、并发和后台线程是三个不同概念。掌握它们的边界比记住所有 API 更有用。

用你的前端经验理解

async/await 很像 Promise 写法,但 Swift 还有结构化并发、任务取消和 Actor 隔离;await 不是“切到后台”的指令。

为什么需要它

异步主要解决“等待期间可以让出执行机会”,不是为代码自动安排后台线程。网络请求等待服务器时适合挂起;巨大的图片处理循环即使写在 async 函数里也可能占用执行器。前端的 Promise 经验可以帮助理解结果与错误,但 Swift 还会检查跨隔离域访问可变数据是否安全。

它是怎样工作的

Task 表达异步工作的生命周期,Actor 保护其隔离状态免于同时访问,MainActor 常用于 UI 相关状态。await 是可能挂起的位置,恢复时外部状态可能已经变化;Actor 也不能保证跨多个 await 的业务操作整体原子。取消是协作式信号,任务需要传播取消或在适当位置检查,不能认为取消按钮会强制中断所有计算。

把关键概念连起来

1

async/await

表示函数可以挂起等待;await 处可能让出执行机会。CPU 密集计算不会因为标记 async 就不阻塞 UI。

2

Task

用 .task 把异步工作绑定到 SwiftUI 生命周期;任务取消是协作式的,操作需要响应取消。async let 可并发发起独立任务。

3

Actor

隔离可变状态;@MainActor 常用于 UI 状态。Sendable 描述可安全跨隔离域传递的值,具体诊断受 Swift 模式与设置影响。

Swift · 示例片段
func loadBoth() async throws {    async let profile = loadProfile()    async let articles = loadArticles()    let result = try await (profile, articles)    // 使用 result;两个独立请求可并发等待}
  • async let 启动有共同作用域的子任务;两个输入应确实独立,否则不能为了缩短时间强行并发。
  • try await 在汇合点等待结果并传播错误。示例中的加载函数需自行实现,结果使用位置也必须符合隔离要求。

用于理解当前概念的代码片段;部分示例需要放入对应工程与作用域,并补齐上下文。

搜索输入中的旧结果覆盖问题

  1. 用户先搜索 Swift,紧接着搜索 SwiftUI。如果两个请求并行,前一个更慢的响应可能最后回来覆盖新结果。
  2. 让查询参数驱动 .task(id:),参数变化时旧任务会被取消;请求层传播取消,更新界面前必要时检查取消状态或当前查询身份。
  3. 把 loading、结果和错误的更新放在正确隔离域;不要为了消除诊断使用 unchecked 或 detached。

这里需要你亲自判断

不要随手加 Task.detached 或 @unchecked Sendable 来消除诊断;先查清数据所有权与执行隔离。

做一个小练习

在实验室比较顺序等待和并发等待;解释为什么总耗时不一定相加。

  1. 用不同延迟模拟两个搜索请求,确保最终显示最后一次输入对应的结果。
  2. 标出每个 await 前后依赖的状态,并说明哪些假设可能因挂起而失效。
展开参考思路与验收标准

最终显示 SwiftUI 的结果。取消不是普通“请求失败”,用户主动换搜索词时通常不应弹错误。两个独立请求可用 async let 并发等待;有先后依赖的请求则必须按顺序组织。

打开对应交互实验

给重计算函数加上 async,就一定在后台执行吗?

继续查阅官方资料
Swift 官方文档Swift 并发与隔离

本课聚焦核心认知。具体 API、系统要求与发布政策,以当前官方文档和工程验证为准。

答对理解题后即可标记完成