返回文章列表

Swift Concurrency 系列 02|async/await 到底解决了什么问题?

它不是单纯让代码更短,而是让异步系统重新具备被长期维护的条件

如果只看表面,async/await 好像主要解决三个问题:

  • 回调太多
  • 嵌套太深
  • 代码太丑

但真正在项目里用一段时间后,你会发现这些都只是表象。
它真正解决的是:异步业务终于可以像正常业务一样被组织、被推理、被复用。

这不是表达上的小修小补,而是工程可维护性的变化。

一、它首先解决的是:业务流程不再被写法绑架

很多异步业务本身并不复杂,复杂的是旧写法把它写复杂了。

比如一个真实但常见的流程:

  1. 拉用户资料
  2. 检查会员状态
  3. 再拉首页推荐内容
  4. 如果其中一步失败,回退到兜底展示
  5. 最后更新页面状态

这本来是一个非常清楚的顺序过程。

但在 completion 时代,你经常不得不先处理这些“写法问题”:

  • 成功分支写在哪
  • 失败分支写在哪
  • 主线程切换写在哪
  • 哪一层负责提早 return
  • 哪一层负责统一弹错

于是复杂度不再来自业务,而是来自表达方式本身。
async/await 最直接的收益,就是把这层额外噪音拿掉,让业务复杂度重新回到业务本身。

二、它让“依赖关系”重新变得清楚

很多异步流程不是并行的,而是强依赖的。

例如:

  • 拿到登录态后,才能拿用户资料
  • 拿到用户资料后,才能决定首页加载哪些模块
  • 拿到实验配置后,才能决定页面展示路径

completion 当然也能表达这些关系,但问题在于:它经常把依赖关系藏在层层嵌套里。
代码能跑,但依赖链条不再清晰。

async/await 的一个核心价值,就是把这种“谁依赖谁”的关系重新拉回线性控制流:

let session = try await authService.loadSession()
let user = try await userService.loadUser(session: session)
let modules = try await homeService.loadModules(for: user)

这段代码的价值不只是好看,而是你能立刻看出:

  • 顺序关系
  • 中断点
  • 哪一步可能抛错

这会直接影响后期修改需求时的信心,因为你终于能确定自己动的那一段在整条链路里是什么位置。

三、它显著改善了错误传播这件事

旧异步模型里,错误处理最容易碎。

因为每一层 completion 都可能失败,最后经常会形成这种局面:

  • 第一层请求失败一套处理
  • 第二层失败另一套处理
  • 第三层失败再来一套
  • 有的地方吞错
  • 有的地方弹 toast
  • 有的地方只打日志

你会发现,真正麻烦的不是“有错误”,而是错误路径和主流程一样被拆散了

async/await 至少给了你统一的错误传播模型:

  • 这一层处理掉
  • 还是继续向上抛
  • 由更高层统一兜底

也就是说,它并没有替你决定“该怎么处理错误”,但它至少让“错误怎么流动”重新变得清楚。这对业务边界尤其重要。

四、它把暂停点显式暴露出来了

很多人会忽略 await 最重要的一个意义:
它不是装饰词,而是一个提醒。

它在告诉你:

  • 这里可能暂停
  • 这里的上下文可能变化
  • 这里之后的代码,不应该默认还处在完全相同的环境里

这对并发思维很关键。

因为异步代码真正危险的地方,往往不是调用本身,而是你在调用之后还下意识以为:

  • 当前页面还在
  • 当前状态还没变
  • 当前对象还保持原样

await 至少在语法上把“这里是边界”明确标出来了。
这会迫使你开始认真思考状态有效性,而不是把异步当普通函数调用看待。

五、它让取消这件事更容易进入主设计,而不是外挂逻辑

过去很多代码也能做取消,但取消通常像是后补功能:

  • 单独存一个 token
  • 页面销毁时记得 cancel
  • 某些场景手动把旧请求停掉

能不能做?当然能。
但问题是取消在旧模型里经常不属于主流程,而像一个外置补丁。

到了 Swift Concurrency 里,任务和取消终于更自然地被看成一组东西。
这会迫使你更早问出几个关键问题:

  • 这个任务归谁拥有
  • 页面离开时它该不该停
  • 新任务来了,旧任务该不该失效

它没有自动帮你把取消做对,但它把“取消应该被设计进去”这件事抬到了更前面。

六、它提升的不只是个人体验,而是团队协作的一致性

这是很多人低估的一点。

一个人写代码时,completion 和 async/await 的差异可能只体现为顺不顺手。但在多人协作项目里,async/await 真正重要的地方是它让团队表达方式变得更统一。

现在你至少可以默认:

  • 异步能力会出现在 async 函数里
  • 失败用 throw
  • 暂停点用 await

这种统一非常重要。
因为复杂项目里最怕的不是“技术点没学会”,而是每个人都用自己的一套异步组织方式。那样项目迟早会变成能跑,但没人能快速接手。

七、它没有解决什么

讲到这里也要反过来说清楚,async/await 没有解决这些问题:

  • 它不会自动消除竞态条件
  • 不会自动防止旧请求覆盖新结果
  • 不会替你决定状态边界应该怎么划
  • 不会自动保证 UI 更新在正确上下文里发生

所以如果一个项目原本异步设计就很乱,换成 async/await 后也可能只是“更好看地乱”。

这点一定要看清。
async/await 不是架构灵药,它只是把很多本来被写法掩盖的问题重新照亮了。

八、结论:它解决的是“异步代码还能不能被长期维护”

如果只用一句话总结,我会说:

async/await 真正解决的,不是回调太丑,而是异步代码在复杂项目里越来越难被组织、被推理、被维护。

所以更准确的说法不是:

“它让代码更短。”

而是:

“它让异步代码重新具备了长期可维护的结构条件。”