← 首页 / RxJS · 从 Observable 标准契约,到它解决的那些问题 待审核 7 页

RxJS · 从 Observable 标准契约,到它解决的那些问题

RxJS 常被当成「一大堆 operator」来记,但它的内核其实只有一个标准契约:Observable —— 一个可以脱离库、用十几行就手写出来的接口。subscribe(observer) 触发一个 producer,producer 通过 observer.next / error / complete 把值给你,并返回一个 teardown 作为 Subscription取消。这正是 TC39 Observable proposal、以及如今 WHATWG DOM Observable (el.when('click')) 的形状。

本系列从这个契约本身出发: 先把它放进 singular/plural × pull/push标准坐标系里,看清它和 function / Iterator / Promise 的分工; 再依次拆 创建 → 变换 (operator) → 组合 (高阶 flatten) → 共享 (hot/cold); 最后落到工程里真正要解决的问题 —— 取消陈旧请求、防抖 autocomplete、拖拽、重试。每页都用 marble diagram 单步观察流随时间的演化。

标准接口:Observable 到底是什么 contract · Observable / Observer / Subscription

契约:十几行手写一个 Observable

不引入任何库,从零手写 Observable:subscribe(observer) 调用 producer,producer 通过 observer.next / error / complete 推值,返回的 teardown 就是 Subscription。单步执行看值如何被推给 observer,并用 marble 画出这条流。对照 Promise 点出三处本质差异:lazy(订阅才执行)、多值 (next*)、可取消(unsubscribe 触发 teardown),以及语法契约 next* (error | complete)?

four-kinds · 标准坐标系 2×2

坐标系:function / Iterator / Promise / Observable

把四种取值方式放进 singular / plural×pull / push 的 2×2 网格:单值+拉 = function,多值+拉 = Iterator,单值+推 = Promise,多值+推 = Observable。点每一格,右侧用真实代码演示各自的取值接口 (fn() / it.next() / p.then / obs.subscribe)。看清 Observable 补上的正是右下角那格——它是 Promise 在「多值」维度上的自然推广。

构造与变换:从值到流,再变形 creation · of / from / interval / timer / fromEvent

创建:把任何东西变成一条流

marble 引擎首次登场:选 of / from(iterable 或 promise)/ interval / timer / fromEvent,看它在时间轴上如何吐出 marble。重点是 cold + lazy——Observable 只是「配方」,不订阅什么都不会发生;每次 subscribe 都把 producer 从头重新执行一遍(页面用订阅计数演示),这与 hot 流的共享执行相对。

operators · pipe / map / filter / scan / take

变换:pipe 把 operator 串成管道

上游 marble → operator → 下游 marble。单步推进时间游标,逐个看 map / filter / scan / take / takeWhile 如何逐值变换。核心观念:operator 是纯函数,吃一个 Observable 返回一个新的 Observable,不改上游;pipe(a, b, c) 只是函数组合,数据从上到下流过每一节。

组合与共享:多条流拧到一起 flattening · mergeMap / concatMap / switchMap / exhaustMap

高阶:一个值映射成一条流,四种拍平策略

map 的结果本身是 Observable,就得到高阶 Observable,需要 flatten 成一条流。同一条外层输入,四种策略并排对比:mergeMap 全部并发合并、concatMap 排队串接、switchMap 新的一来就取消旧的exhaustMap 忙时忽略新的。这页是理解「消除请求竞态」的关键——autocomplete 为何用 switchMap,答案就在这四条 marble 的差异里。

hot-cold · Subject / share / multicast

冷与热:独立重放,还是共享一次执行

cold Observable 每个订阅者各触发一次独立执行(两条互不相干的 marble);hot 流(如 Subject)多个订阅者共享同一次执行,晚订阅者会错过之前的值。用两个订阅者并排演示差异,并说明 share / multicast 如何把一条 cold 流转成 hot——避免同一份网络请求被每个订阅者重复触发。

解决哪些问题:落到真实工程 applications · 竞态 / 防抖 / 拖拽 / 重试

落地:这些问题,用流怎么优雅解决

把前面的契约与 operator 对上真实痛点,每个做成可交互 lab: autocompletedebounceTime 合并抖动 + distinctUntilChanged + switchMap 丢弃陈旧响应,当场看竞态被消掉)、拖拽mousedownswitchMap(mousemove 直到 mouseup),用 takeUntil 收尾)、retry / backoff(失败后延迟重订阅)。末尾一张「问题 → operator」速查表。

一句话串起来

RxJS 的地基是一个标准契约 Observable —— 推送式、惰性、可取消的多值序列 (见 契约),它在 pull/push × 单值/多值 坐标系里正好补上 Promise 缺的那格 (见 坐标系)。有了契约就能把任何东西创建成 cold 流 (见 创建),用 pipe 里的纯函数 operator 变换 (见 变换);当值映射出的又是流,就用四种 flatten 组合 (见 高阶),用 Subject / share 决定是否共享执行 (见 冷与热)。这一切最终是为了优雅解决工程里的竞态、防抖、取消、重试 (见 落地)。

顺着标准与源头读下去

官方文档 / 教程

  • RxJS · Observable 指南 rxjs.dev 官方对 Observable / Observer / Subscription / Subject / operator 的权威说明,marble 与代码对照。
  • Learn RxJS learnrxjs.io 按 operator 分类的速查与配方,每条附可运行示例;查「该用哪个 operator」的好去处。
  • RxMarbles rxmarbles.com 可交互 marble diagram:拖动源 marble 实时看各 operator 的输出,本系列可视化的灵感来源。

标准接口的源头

  • TC39 · Observable proposal github.com 把 Observable 纳入 ECMAScript 的提案,定义了 subscribe / Observer / Subscription 的最小契约。
  • MDN · Observable (DOM) developer.mozilla.org WHATWG 把 Observable 引入 DOM:EventTarget.when() 直接返回事件的 Observable,原生的 fromEvent
  • MDN · Streams API developer.mozilla.org 对照另一套「流」抽象:ReadableStream 是 pull-based + backpressure,与 Observable 的 push 模型形成互补。