Web 平台 API / RxJS · 从 Observable 标准契约,到它解决的那些问题 / 契约:十几行手写一个 Observable 待审核 1 / 7
contract · Observable / Observer / Subscription

契约:十几行手写一个 Observable

RxJS 的地基不是一大堆 operator,而是一个标准契约。它小到可以脱离库、用十几行手写出来:Observable 持有一段 producer 逻辑,只有在 subscribe 时才执行;producer 拿到一个 Observer(带 next / error / complete 的对象),通过它把值推出去;subscribe 返回一个 Subscription(一个能 unsubscribe 的句柄),用来随时取消。

这也是 TC39 Observable proposal 与 WICG 的 DOM Observable 提案(EventTarget.when())的形状。后者仍是实验特性,仅 Chromium 135+(2025-04)落地,Firefox 与 Safari 均未实现(兼容性据 MDN BCD,核对于 2026-08)。下面这十行就是整个 RxJS 的种子:

class Observable {
  constructor(onSubscribe) {
    this.onSubscribe = onSubscribe;
  }
  subscribe(observer) {
    const teardown = this.onSubscribe(observer);
    return { unsubscribe: () => teardown && teardown() };
  }
}

1 · producer 把值推给 observer

整段 producer 在 subscribe 的那一刻才开始跑,这是 Observable 与立即执行的普通函数最不同的地方。每调用一次 observer.next(v),observer 就收到一个值、时间轴上多一颗 marble;最后 complete() 用竖线封口。

图 1-1 · producer 逐行执行时值如何被推给 observer,并同步画成 marble。可单步推进观察 nextcomplete 的落点。

2 · 事件序列的语法约束

一条流的事件序列受一条简单语法约束:可以推任意多个 next,之后至多用一个 error 或一个 complete 收尾。二者互斥,且一旦收尾就不再有任何事件。这条契约让下游可以放心地在 completeerror 里做清理。

1  2  3  |      合法:三个 next 后 complete
1  2  ✕         合法:两个 next 后 error 收尾
|               合法:一个值都不推,直接 complete(空流)
1  |  2         非法:complete 之后不能再有 next
|  ✕            非法:complete 与 error 不能同时出现

本系列的图形记号用 表示 error(与 SVG 渲染器画的叉一致)。RxJS TestScheduler 的 ASCII marble 记号里,error 写作 #、complete 写作 |,同帧的多个值用括号成组,如 of(1,2,3)(123|) [1]。

3 · unsubscribe 与 teardown

producer 可以返回一个 teardown 函数,subscribe 把它包进 Subscription。调用 unsubscribe() 就会运行这个 teardown。它具体做什么完全由 producer 决定——可能停掉定时器、解掉事件监听,也可能什么都不做。这一点常被误传成「unsubscribe 会取消在途的网络请求」:能否真的 abort 取决于 teardown 里写了什么,fromFetch 会(它内部挂了 AbortController),而把一个 promise 包成 Observable 的写法不会,请求照跑到底,只是结果被丢弃。这在展平一页还会再遇到。

Promise 连这个选择都没有:一旦发起就无法取消。

图 3-1 · 一条每 600ms 推一个值的 interval$,可随时取消。可点取消观察 teardown 何时运行、marble 何时停止增长。

建议 · 对照 Promise 有三处本质差异。惰性:new Promise(fn) 里的 fn 立刻同步执行,而 Observable 的 producer 要 subscribe 才执行,且每次订阅各跑一遍。多值:Promise 只 resolve 一个值,Observable 能推任意多个 next。可取消:Promise 无法取消,Observable 用 unsubscribe 触发 teardown 收尾。四象限坐标系一页把这两者放进同一张表里比较。

4 · 参考文献

  1. RxJS Documentation. Marble Testing(ASCII marble 记号与同帧成组写法). ReactiveX.
  2. WICG. ObservableEventTarget.when() 提案). Web Incubator CG.
  3. TC39. Observable proposal. Ecma International.