乐闻世界logo
搜索文章和话题

RxJS 中 Hot Observable 和 Cold Observable 有什么区别?

2026年2月21日 16:59

Hot Observable vs Cold Observable

Cold Observable(冷 Observable)

定义: Cold Observable 是惰性的,每个订阅者都会独立执行 Observable 的逻辑。

特点:

  • 每个订阅者都会获得独立的数据流
  • 订阅时才开始执行
  • 不共享数据
  • 生产者不会主动推送数据

示例:

javascript
import { Observable } from 'rxjs'; const cold$ = new Observable(subscriber => { console.log('Observable executed'); subscriber.next(Math.random()); subscriber.complete(); }); cold$.subscribe(value => console.log('Subscriber 1:', value)); // Observable executed // Subscriber 1: 0.123456 cold$.subscribe(value => console.log('Subscriber 2:', value)); // Observable executed // Subscriber 2: 0.789012 // 注意:每次订阅都重新执行,产生不同的随机数

常见的 Cold Observable:

  • of()
  • from()
  • interval()
  • timer()
  • ajax()
  • http.get() (Angular)
  • 大多数创建操作符

Hot Observable(热 Observable)

定义: Hot Observable 是主动的,多个订阅者共享同一个数据流。

特点:

  • 所有订阅者共享同一个数据流
  • 即使没有订阅者也会执行
  • 共享数据
  • 生产者主动推送数据

示例:

javascript
import { Observable, Subject } from 'rxjs'; const hot$ = new Observable(subscriber => { console.log('Observable executed'); subscriber.next(Math.random()); subscriber.complete(); }); const subject = new Subject(); hot$.subscribe(subject); subject.subscribe(value => console.log('Subscriber 1:', value)); // Observable executed // Subscriber 1: 0.123456 subject.subscribe(value => console.log('Subscriber 2:', value)); // Subscriber 2: 0.123456 // 注意:两个订阅者收到相同的值

常见的 Hot Observable:

  • Subject 及其变体
  • BehaviorSubject
  • ReplaySubject
  • AsyncSubject
  • DOM 事件(通过 fromEvent
  • WebSocket 连接
  • share() 转换后的 Observable

转换方法

1. 使用 share() 将 Cold 转换为 Hot

javascript
import { interval } from 'rxjs'; import { share, take } from 'rxjs/operators'; const cold$ = interval(1000).pipe( take(5) ); const hot$ = cold$.pipe( share() // 转换为 Hot Observable ); hot$.subscribe(value => console.log('Subscriber 1:', value)); hot$.subscribe(value => console.log('Subscriber 2:', value)); // 两个订阅者共享同一个数据流

2. 使用 shareReplay() 缓存值

javascript
import { interval } from 'rxjs'; import { shareReplay, take } from 'rxjs/operators'; const hot$ = interval(1000).pipe( take(5), shareReplay(1) // 缓存最后一个值 ); hot$.subscribe(value => console.log('Subscriber 1:', value)); setTimeout(() => { hot$.subscribe(value => console.log('Subscriber 2:', value)); // 新订阅者会立即收到缓存的值 }, 3000);

3. 使用 publish() 和 connect()

javascript
import { interval } from 'rxjs'; import { publish, take } from 'rxjs/operators'; const cold$ = interval(1000).pipe( take(5) ); const hot$ = cold$.pipe( publish() // 转换为 Hot Observable ); hot$.subscribe(value => console.log('Subscriber 1:', value)); hot$.subscribe(value => console.log('Subscriber 2:', value)); hot$.connect(); // 开始执行

实际应用场景

Cold Observable 适用场景

  1. HTTP 请求
javascript
// 每次订阅都会发起新的请求 http.get('/api/data').subscribe(data => { console.log('Request 1:', data); }); http.get('/api/data').subscribe(data => { console.log('Request 2:', data); });
  1. 独立的数据处理
javascript
// 每个订阅者需要独立的数据流 of(1, 2, 3).pipe( map(x => x * 2) ).subscribe(value => console.log(value));
  1. 需要重新执行的场景
javascript
// 每次订阅都重新计算 const calculation$ = new Observable(subscriber => { const result = expensiveCalculation(); subscriber.next(result); subscriber.complete(); });

Hot Observable 适用场景

  1. 共享数据
javascript
// 多个组件共享同一个数据流 const userData$ = http.get('/api/user').pipe( share() ); component1.userData$.subscribe(user => { console.log('Component 1:', user); }); component2.userData$.subscribe(user => { console.log('Component 2:', user); }); // 只发起一次请求,两个组件共享结果
  1. 事件流
javascript
// 多个订阅者监听同一个事件 const click$ = fromEvent(document, 'click').pipe( share() ); click$.subscribe(event => { console.log('Handler 1:', event); }); click$.subscribe(event => { console.log('Handler 2:', event); });
  1. WebSocket 连接
javascript
// 多个订阅者共享同一个 WebSocket 连接 const socket$ = webSocket('ws://localhost:8080').pipe( share() ); socket$.subscribe(message => { console.log('Handler 1:', message); }); socket$.subscribe(message => { console.log('Handler 2:', message); });

性能对比

Cold Observable 性能特点

优点:

  • 每个订阅者获得独立的数据流
  • 不会相互影响
  • 适合需要独立处理的场景

缺点:

  • 可能重复执行相同的操作
  • 浪费资源(如重复的 HTTP 请求)
  • 内存占用可能更高

Hot Observable 性能特点

优点:

  • 共享数据流,避免重复执行
  • 节省资源(如只发起一次 HTTP 请求)
  • 内存占用更低

缺点:

  • 订阅者可能错过之前的数据
  • 需要管理订阅时机
  • 可能出现竞态条件

选择指南

使用 Cold Observable 当:

  • 每个订阅者需要独立的数据流
  • 需要重新执行操作
  • 订阅者之间不应该相互影响
  • 数据源是按需生成的

使用 Hot Observable 当:

  • 多个订阅者需要共享数据
  • 需要避免重复执行(如 HTTP 请求)
  • 数据源是主动推送的(如事件、WebSocket)
  • 需要缓存数据供后续订阅者使用

最佳实践

1. HTTP 请求共享

javascript
// ❌ 错误:每次订阅都发起请求 class UserService { getUser(id: string) { return http.get(`/api/users/${id}`); } } // ✅ 正确:共享请求结果 class UserService { private cache = new Map<string, Observable<User>>(); getUser(id: string) { if (!this.cache.has(id)) { this.cache.set(id, http.get(`/api/users/${id}`).pipe( shareReplay(1) )); } return this.cache.get(id)!; } }

2. 事件处理

javascript
// 使用 share() 共享事件流 const resize$ = fromEvent(window, 'resize').pipe( debounceTime(200), share() ); resize$.subscribe(event => { updateLayout1(event); }); resize$.subscribe(event => { updateLayout2(event); });

3. 状态管理

javascript
// 使用 BehaviorSubject 管理状态 const state$ = new BehaviorSubject(initialState); state$.subscribe(state => { console.log('Listener 1:', state); }); state$.subscribe(state => { console.log('Listener 2:', state); }); // 更新状态 state$.next(newState);

常见陷阱

1. 忘记共享导致重复请求

javascript
// ❌ 错误示例 const data$ = http.get('/api/data'); data$.subscribe(data => console.log('Component 1:', data)); data$.subscribe(data => console.log('Component 2:', data)); // 发起两次请求 // ✅ 正确示例 const data$ = http.get('/api/data').pipe( share() ); data$.subscribe(data => console.log('Component 1:', data)); data$.subscribe(data => console.log('Component 2:', data)); // 只发起一次请求

2. 错误的共享时机

javascript
// ❌ 错误示例 const data$ = http.get('/api/data').pipe( share() ); // 立即订阅触发请求 data$.subscribe(); // 后续订阅者可能错过数据 setTimeout(() => { data$.subscribe(data => console.log(data)); }, 2000); // ✅ 正确示例 const data$ = http.get('/api/data').pipe( shareReplay(1) // 缓存数据 );

3. 不当使用 shareReplay

javascript
// ❌ 错误示例:缓存过多数据 const data$ = interval(1000).pipe( shareReplay(1000) // 缓存1000个值,占用大量内存 ); // ✅ 正确示例:合理设置缓存大小 const data$ = interval(1000).pipe( shareReplay(1) // 只缓存最后一个值 );

总结

理解 Hot 和 Cold Observable 的区别对于编写高效的 RxJS 代码至关重要:

  1. Cold Observable: 惰性、独立执行、适合按需生成的数据
  2. Hot Observable: 主动、共享执行、适合主动推送的数据
  3. 转换方法: 使用 share()shareReplay() 等操作符进行转换
  4. 性能考虑: Hot Observable 可以避免重复执行,提高性能
  5. 选择原则: 根据场景选择合适的类型,避免不必要的资源浪费

正确使用这两种 Observable 类型,可以显著提升应用的性能和可维护性。

标签:Rxjs