
As Angular developers, we tend to work with RxJS and observables on a daily basis. In this article, I am going to explain hot and cold observables and the differences between them. First, let’s take a look at a couple of definitions for these two terms.
- When the data source is created inside the observable, it is considered cold. Otherwise, if a data source is created outside, it is a hot observable.
- Cold observables are unicast, whereas hot observables are multicast.
- Cold observables start to emit values only when we subscribe to them. Hot ones emit values always, even if there are no subscribers.
Don’t get confused by the definitions. They will be more clear after we create a helpful mental model.
A good analogy from real life is YouTube videos. Every viewer of that YT video has their own execution process. This means that they can watch the video whenever they decide. This process is quite similar to how cold observables work. On the other hand, a live stream is something different. It behaves like a hot observable. It is shared between multiple viewers who watch it at the same time.
const video$ = of(null).pipe(map(() => Math.random()));
video$.subscribe(console.log); // Viewer 1
video$.subscribe(console.log); // Viewer 2
video$.subscribe(console.log); // Viewer 3
What would you expect to be logged into the console?
Three different values are printed! Every subscriber has its own execution context, so they are unicasted. They don’t share the value of Math.random()
. This was a simple example of how cold observables behave. What if we want to turn it from cold 🧊 to hot 🔥?
const video$ = of(null).pipe(
map(() => Math.random()),
shareReplay() // <-- makes the observable hot
);
video$.subscribe(console.log); // 0.9737887545048507
video$.subscribe(console.log); // 0.9737887545048507
video$.subscribe(console.log); // 0.9737887545048507
We used shareReplay()
which turns the cold observable into the hot one and now the randomly generated value will be broadcasted to each subscriber so they get the same value.
The reason why shareReplay
operator makes observable hot is that behind-the-scenes it uses ReplaySubject
and in RxJs any Subject is considered to be hot. The similar happens for other hot operators like share
but there behind-the-scenes a regular Subject
is used.
Another popular operator that creates hot observables is fromEvent()
. This one doesn’t use any RxJs Subjects under the hood, however, the event producer exists always instead of being created inside each observable and it is actually the main criteria for an observable to be a hot one. (It will become clearer later when we will be creating a custom hot/cold operator)👇 But so far…
Which impact it has on real ANGULAR code?
For instance, HTTP calls return cold observables. If we subscribe to it with async
pipe twice in our template, we will get 2 calls to the server. We should somehow turn this cold observable into a hot one to prevent unnecessary requests. We can achieve that by using the operator shareReplay()
we mentioned in the previous paragraph. Now only one request is made to the server.
@Component({
selector: "app-root",
template: `
<main>
<h1>RxJS - Hot / Cold Observable</h1>
<div>Count: {{ (posts$ | async)?.length }}</div>
<ul>
<li *ngFor="let post of posts$ | async">{{ post.title }}</li>
</ul>
</main>
`,
styles: [``],
})
export class AppComponent implements OnInit {
posts$!: Observable<any[]>;
constructor(private http: HttpClient) {}
this.posts$ = this.http
.get<any[]>(`http://jsonplaceholder.typicode.com/posts`)
.pipe(shareReplay());
}
Creating our own Hot and Cold operator
We can also create our custom creation RxJs operator to get a better understanding of how the creation of a data source is related to hot and cold observables. Creation Operators in RxJs are just functions that return Observable
. The following operator gives us back a numeric value between 1-6. It represents the process of dice rolling. 🎲🎲🎲
const rollDice = (): Observable<number> => {
return new Observable((subscriber) => {
const diceNumber = Math.floor(Math.random() * 6 ) + 1; // inside
subscriber.next(diceNumber);
});
}
const dice$ = rollDice();
dice$.subscribe(console.log); // 5
dice$.subscribe(console.log); // 3
dice$.subscribe(console.log); // 2
As we see in the code example, the data source diceNumber
is created inside Observable
. Due to that fact, the diceNumber
is unicasted and every subscriber gets a different value. If we want to make rollDice
operator hot, we have to do just one simple modification to our code. We will move data source creation outside Observable
.
const rollDice = (): Observable<number> => {
const diceNumber = Math.floor(Math.random() * 6 ) + 1; // outside
return new Observable((subscriber) => {
subscriber.next(diceNumber);
});
}
const dice$ = rollDice();
dice$.subscribe(console.log); // 4
dice$.subscribe(console.log); // 4
dice$.subscribe(console.log); // 4
Now, diceNumber
value is being cached and multicasting is achieved. In this way, all potential subscribers will get the same value.
Alright, that would be it for today! I hope you enjoyed it and learned something new. You can also look at this topic in video format right below. Feel free to comment, share knowledge, and get into a discussion in the comment section.

Advanced Angular Forms – Deep Dive
Check out the most Advanced Angular Forms course made by Google Developer Expert in Angular