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

What is the difference between Hot Observable and Cold Observable in RxJS?

2月21日 16:59

Hot Observable vs Cold Observable

Cold Observable

Definition: Cold Observable is lazy, each subscriber independently executes the Observable logic.

Characteristics:

  • Each subscriber gets an independent data stream
  • Execution starts when subscribed
  • Does not share data
  • Producer does not actively push data

Example:

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 // Note: Each subscription re-executes, producing different random numbers

Common Cold Observables:

  • of()
  • from()
  • interval()
  • timer()
  • ajax()
  • http.get() (Angular)
  • Most creation operators

Hot Observable

Definition: Hot Observable is active, multiple subscribers share the same data stream.

Characteristics:

  • All subscribers share the same data stream
  • Executes even without subscribers
  • Shares data
  • Producer actively pushes data

Example:

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 // Note: Both subscribers receive the same value

Common Hot Observables:

  • Subject and its variants
  • BehaviorSubject
  • ReplaySubject
  • AsyncSubject
  • DOM events (via fromEvent)
  • WebSocket connections
  • Observable converted with share()

Conversion Methods

1. Using share() to Convert Cold to Hot

javascript
import { interval } from 'rxjs'; import { share, take } from 'rxjs/operators'; const cold$ = interval(1000).pipe( take(5) ); const hot$ = cold$.pipe( share() // Convert to Hot Observable ); hot$.subscribe(value => console.log('Subscriber 1:', value)); hot$.subscribe(value => console.log('Subscriber 2:', value)); // Both subscribers share the same data stream

2. Using shareReplay() to Cache Values

javascript
import { interval } from 'rxjs'; import { shareReplay, take } from 'rxjs/operators'; const hot$ = interval(1000).pipe( take(5), shareReplay(1) // Cache the last value ); hot$.subscribe(value => console.log('Subscriber 1:', value)); setTimeout(() => { hot$.subscribe(value => console.log('Subscriber 2:', value)); // New subscriber immediately receives cached value }, 3000);

3. Using publish() and connect()

javascript
import { interval } from 'rxjs'; import { publish, take } from 'rxjs/operators'; const cold$ = interval(1000).pipe( take(5) ); const hot$ = cold$.pipe( publish() // Convert to Hot Observable ); hot$.subscribe(value => console.log('Subscriber 1:', value)); hot$.subscribe(value => console.log('Subscriber 2:', value)); hot$.connect(); // Start execution

Practical Use Cases

Cold Observable Use Cases

  1. HTTP Requests
javascript
// Each subscription initiates a new request http.get('/api/data').subscribe(data => { console.log('Request 1:', data); }); http.get('/api/data').subscribe(data => { console.log('Request 2:', data); });
  1. Independent Data Processing
javascript
// Each subscriber needs independent data stream of(1, 2, 3).pipe( map(x => x * 2) ).subscribe(value => console.log(value));
  1. Scenarios Requiring Re-execution
javascript
// Recalculate on each subscription const calculation$ = new Observable(subscriber => { const result = expensiveCalculation(); subscriber.next(result); subscriber.complete(); });

Hot Observable Use Cases

  1. Shared Data
javascript
// Multiple components share the same data stream 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); }); // Only one request initiated, both components share the result
  1. Event Streams
javascript
// Multiple subscribers listen to the same event 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 Connections
javascript
// Multiple subscribers share the same WebSocket connection const socket$ = webSocket('ws://localhost:8080').pipe( share() ); socket$.subscribe(message => { console.log('Handler 1:', message); }); socket$.subscribe(message => { console.log('Handler 2:', message); });

Performance Comparison

Cold Observable Performance Characteristics

Advantages:

  • Each subscriber gets independent data stream
  • No interference between subscribers
  • Suitable for scenarios requiring independent processing

Disadvantages:

  • May repeatedly execute the same operations
  • Wastes resources (e.g., duplicate HTTP requests)
  • Higher memory usage

Hot Observable Performance Characteristics

Advantages:

  • Shared data stream, avoids repeated execution
  • Saves resources (e.g., only one HTTP request)
  • Lower memory usage

Disadvantages:

  • Subscribers may miss previous data
  • Need to manage subscription timing
  • Possible race conditions

Selection Guide

Use Cold Observable when:

  • Each subscriber needs independent data stream
  • Need to re-execute operations
  • Subscribers should not affect each other
  • Data source is generated on demand

Use Hot Observable when:

  • Multiple subscribers need to share data
  • Need to avoid repeated execution (e.g., HTTP requests)
  • Data source is actively pushed (e.g., events, WebSocket)
  • Need to cache data for future subscribers

Best Practices

1. HTTP Request Sharing

javascript
// ❌ Wrong: Each subscription initiates a request class UserService { getUser(id: string) { return http.get(`/api/users/${id}`); } } // ✅ Correct: Share request results 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. Event Handling

javascript
// Use share() to share event stream const resize$ = fromEvent(window, 'resize').pipe( debounceTime(200), share() ); resize$.subscribe(event => { updateLayout1(event); }); resize$.subscribe(event => { updateLayout2(event); });

3. State Management

javascript
// Use BehaviorSubject to manage state const state$ = new BehaviorSubject(initialState); state$.subscribe(state => { console.log('Listener 1:', state); }); state$.subscribe(state => { console.log('Listener 2:', state); }); // Update state state$.next(newState);

Common Pitfalls

1. Forgetting to Share Causes Duplicate Requests

javascript
// ❌ Wrong example const data$ = http.get('/api/data'); data$.subscribe(data => console.log('Component 1:', data)); data$.subscribe(data => console.log('Component 2:', data)); // Two requests initiated // ✅ Correct example const data$ = http.get('/api/data').pipe( share() ); data$.subscribe(data => console.log('Component 1:', data)); data$.subscribe(data => console.log('Component 2:', data)); // Only one request initiated

2. Wrong Sharing Timing

javascript
// ❌ Wrong example const data$ = http.get('/api/data').pipe( share() ); // Immediate subscription triggers request data$.subscribe(); // Later subscribers may miss data setTimeout(() => { data$.subscribe(data => console.log(data)); }, 2000); // ✅ Correct example const data$ = http.get('/api/data').pipe( shareReplay(1) // Cache data );

3. Improper Use of shareReplay

javascript
// ❌ Wrong example: Caching too much data const data$ = interval(1000).pipe( shareReplay(1000) // Cache 1000 values, uses a lot of memory ); // ✅ Correct example: Reasonable cache size const data$ = interval(1000).pipe( shareReplay(1) // Only cache the last value );

Summary

Understanding the difference between Hot and Cold Observables is crucial for writing efficient RxJS code:

  1. Cold Observable: Lazy, independent execution, suitable for on-demand generated data
  2. Hot Observable: Active, shared execution, suitable for actively pushed data
  3. Conversion Methods: Use operators like share(), shareReplay() for conversion
  4. Performance Considerations: Hot Observable can avoid repeated execution, improving performance
  5. Selection Principle: Choose appropriate type based on scenario, avoid unnecessary resource waste

Proper use of these two Observable types can significantly improve application performance and maintainability.

标签:Rxjs