6-20.【架构设计】Combine / Rx 中,map / flatMap / switchLatest 的本质区别是什么?在架构层面你如何选择?

0 阅读2分钟

一、本质区别

假设有一个 Publisher(Combine / Rx):

sourcePublisher: A

你想根据它生成 B 类型的数据流,可以用 map / flatMap / switchLatest,但它们的处理方式本质不同


1️⃣ map

sourcePublisher.map { a in f(a) }
  • 本质:一对一转换

  • 类型A -> B

  • 行为

    • 每个输入事件 a → 输出 f(a)
    • 输出流长度 = 输入流长度
  • 异步性

    • 输出值同步产生(除非 f 返回 Publisher,再用 flatMap 包裹)
  • 特点:简单、轻量、不会改变事件流数量

💡 工程直觉:用来做单次同步转换,比如 Int -> StringModel -> ViewModel


2️⃣ flatMap

sourcePublisher.flatMap { a in asyncPublisher(a) }
  • 本质:把一个事件映射成一个新的 Publisher,并把这些内部 Publisher 的事件“合并”到同一个输出流

  • 类型A -> Publisher<B> → 输出 B

  • 行为

    • 所有内部 Publisher 并行订阅
    • 输出事件顺序可能交错
  • 异步性

    • 支持异步任务、网络请求等
  • 特点

    • 每个输入事件触发一个新的 Publisher
    • 输出流混合所有内部 Publisher 的事件
  • 🔥 风险:高频输入 → 多个并行网络请求 → 可能资源浪费 / 并发混乱

💡 工程直觉:输入事件触发独立异步任务,需要并行处理并保留所有结果


3️⃣ switchLatest (Combine) / flatMapLatest (Rx)

sourcePublisher
  .map { a in asyncPublisher(a) }
  .switchToLatest()
  • 本质:内部 Publisher 可切换,只保留最新的内部 Publisher 的输出

  • 行为

    • 新输入事件 → 取消上一个内部 Publisher
    • 输出只来自最新 Publisher
  • 异步性

    • 适合“只关心最新任务结果”场景
  • 特点

    • 避免旧任务干扰 UI
    • 控制并发资源占用

💡 工程直觉:输入是高频事件(如搜索、滑动、输入) → 只关心最新结果


二、行为总结表

Operator输入 → 输出并发保留旧事件典型场景
map1:1 转换N/A数据映射 / Model → ViewModel
flatMap1 → Publisher → 输出合并并行并行网络请求 / 多任务汇总
switchLatest1 → Publisher → 输出最新串行(旧取消)否(取消旧任务)高频输入 → 只保留最新结果

三、架构层面的选择原则

1️⃣ map → 同步 / 派生状态

  • 用于 State → DerivedStateModel → ViewModel
  • 不改变事件流结构
  • 性能轻,预测性好

2️⃣ flatMap → 并行异步操作

  • 用于 State 触发多任务

  • 保留所有结果 → Reducer 或 Adapter 处理

  • 注意:

    • 并发数量控制(避免 flood / race)
    • 输出混合 → Reducer 必须能处理乱序数据

3️⃣ switchLatest → 高频事件 + 最新任务

  • 用于 UI 高频事件(搜索框、滑动、节流动画)
  • 自动取消旧任务 → 减少资源占用
  • 可与 State / Reducer 结合 → 保持 UI 一致性

四、架构落地直觉

输入事件 → 映射转换 → 异步任务 → Reducer / Store
  • map → State 派生 / ViewModel 映射
  • flatMap → 并行 Effect / 网络 / DB
  • switchLatest → 高频输入 → 异步请求 → 保持最新 UI

核心原则:选择 Operator = 根据事件频率 + 是否保留历史 + 是否需要并行处理