冷与热:独立重放,还是共享一次执行
同样是一条 Observable,订阅它可能触发两种截然不同的行为。cold(冷)流把 producer 逻辑攥在自己手里:每次
subscribe
都各跑一遍,每个订阅者拿到独立、完整、互不相干的一份值。hot(热)流的值独立于订阅产生,多个订阅者共享同一次执行——晚订阅者会错过订阅前已经发出的值。分清冷热,是避免「同一份网络请求被每个订阅者重复触发」这类问题的前提。
1 · cold:每个订阅者各触发一遍,独立重放
下面是一条 interval 形态的 cold 流。订阅者 A 在 t=0 订阅,B 在 t=3 订阅。因为是冷流,B 的订阅会让 producer 从头重新跑一遍——所以 A、B 各拿到一份值序列,都从 0 开始计,彼此毫无关系。拖动时间游标,注意两条轨道各自独立地从各自的订阅时刻起步。
2 · hot:共享一次执行,晚订阅错过早值
换成 hot 流(如 Subject):源在后台持续发值,产生过程和谁订阅无关。A 在 t=0 就订阅,收到全部;B 在 t=3 才订阅,只能收到
的值——t=0,1,2 那三个在它订阅前就已经发出并溜走了(下图中 B 轨道上这三格是空缺的)。A、B 共享的是同一串值,不是各自重放。
3 · Subject:既是 Observer,又是 Observable
热流的典型载体是 Subject:它既是 Observer(可以 subject.next(v) 手动推值)、又是 Observable(可以被 subscribe)。因此它天然是「一处推、多处收」的多播中枢。下面这段里,A 先订阅,推 1、2;之后 B 才订阅,再推
3:
结果:A 收到 1, 2, 3,B 只收到 3。B 订阅前推出的 1、2 对它来说已经错过——这正是热流的语义。把一条 cold 流经 share() / multicast 包一层,内部就是用 Subject 把单次执行多播出去,从而避免每个订阅者都重跑一遍 producer(比如重复触发同一份网络请求)。
4 · 小结:冷 vs 热
| 维度 | cold(冷) | hot(热) |
|---|---|---|
| 谁触发执行 | 每次 subscribe 各触发一次 | 执行独立于订阅,后台已在跑 |
| 订阅者是否共享 | 不共享,各跑一份 | 共享同一次执行 |
| 晚订阅是否错过 | 不会,从头完整重放 | 会,只收订阅后的值 |
| 典型例子 | http / of / interval | Subject / DOM 事件 / share() 后 |
冷热不是 Observable 的固有标签,而是「值从哪来」决定的。 producer 在 subscribe 内部新建资源(定时器、请求)→ 冷;producer 只是转发一个早已存在、独立运行的源(Subject、DOM)→ 热。冷流的惰性与独立执行见 创建;把冷热选择用在真实场景(去重请求、状态多播)见 落地。