6-24.【架构设计】并发(async/await)出现后,响应式架构是否“过时”?如何在一个项目中同时使用它们而不混乱?

3 阅读2分钟

一、本质对比:async/await vs 响应式流

特性async/await响应式流 (Combine/Rx)
时间模型单值异步操作,顺序完成多值随时间流动,可组合、可取消
核心问题“如何写顺序异步逻辑”“如何处理随时间变化的状态依赖和组合”
优势语法简洁,调试方便自动依赖追踪、组合、取消、高级算子
劣势高频/流事件组合困难单值顺序逻辑写起来可能繁琐
取消支持Task.cancel()switchLatest / cancellables / DisposeBag

结论响应式架构不是“异步的高级写法”,async/await 也不是“状态传播框架”。
两者解决的问题本质不同。

  • async/await = 执行异步任务的语法
  • 响应式架构 = 状态/事件流的管理、组合和传播

二、互补关系

  1. 异步任务作为 Effect / Publisher / Observable 的源头

    • TCA / Combine 中常用:

      func fetchItems() -> AnyPublisher<[Item], Error> {
          Future { promise in
              Task {
                  let data = await api.fetch()
                  promise(.success(data))
              }
          }.eraseToAnyPublisher()
      }
      
    • async/await 做具体任务逻辑

    • 响应式架构做状态流管理、UI 更新、组合、取消

  2. 状态管理仍然用响应式链条

    • UI / State / DerivedState / Action 流保持响应式
    • async/await 只是产生新的 Action 或 Effect
  3. 高频事件 / 用户输入仍然用响应式

    • 搜索框、滑动、动画、定时器
    • async/await 单值不方便组合和取消 → switchLatest / debounce 仍必需

三、工程实践:共存模式

1️⃣ 用 async/await 做单值异步任务

  • 网络请求、DB、文件读写
  • 简洁、调试方便
  • 包装成 Effect 发回 Action → 响应式流管理状态
func loadItemsEffect() -> Effect<[Item], Never> {
    .task {
        let items = await api.fetchItems()
        return .itemsLoaded(items)
    }
}

2️⃣ 高频状态 / 多值流仍用响应式

  • 用户输入、搜索、滚动、动画
  • switchLatest + flatMapLatest + debounce
  • 保证 UI 一致性和取消能力
searchPublisher
    .debounce(for: 0.3, scheduler: RunLoop.main)
    .map { query in loadItemsEffect(query) }
    .switchToLatest()

3️⃣ 将 async/await 任务嵌入响应式链条

  • async/await = 内部逻辑
  • Publisher / Effect = 状态流管理
  • 保证取消、组合、可测试

4️⃣ 避免混乱的原则

问题解决方式
状态分散所有状态变化仍通过 Action → Reducer → State
副作用无序async/await 任务只通过 Effect 发回 Action,不直接修改 State
高频事件冲突高频输入仍走响应式操作(debounce / switchLatest)
调试复杂保留日志 / snapshot / 单向数据流

核心原则:异步任务 = 实现细节,响应式架构 = 状态管理 + 流组合 + 取消能力


四、直觉总结

  1. 响应式架构没有过时

    • 它解决的是 状态随时间变化的组合、传播、取消、依赖问题
    • async/await 无法替代这些功能
  2. async/await + 响应式 = 最佳实践

    • async/await 写任务逻辑
    • 响应式流管理 State / UI / 高频事件 / 组合 / 取消
    • 保持可预测、可测试、可取消
  3. 关键:不要把 async/await 直接改写为状态驱动逻辑

    • 否则就会出现“响应式外壳 + 命令式内核”的混乱