Hot vs Cold Observables

TL;DR: You want a HOT observable when you don’t want to create your producer over and over again.

Ben Lesh
Ben Lesh
Mar 28, 2016 · 6 min read

COLD is when your observable creates the producer

// COLD
var cold = new Observable((observer) => {
var producer = new Producer();
// have observer listen to producer here
});

HOT is when your observable closes over the producer

// HOT
var producer = new Producer();
var hot = new Observable((observer) => {
// have observer listen to producer here
});

Getting deeper into what’s going on…

Observables are just functions!

What’s a “Producer”?

Cold Observables: Producers created *inside*

  1. creates the producer
  2. activates the producer
  3. starts listening to the producer
  4. unicast

The example below is “cold” because it creates and listens to the WebSocket inside of the subscriber function that is called when you subscribe to the Observable:

const source = new Observable((observer) => {
const socket = new WebSocket('ws://someurl');
socket.addEventListener('message', (e) => observer.next(e));
return () => socket.close();
});

So anything that subscribes to `source` above, will get its own WebSocket instance, and when it unsubscribes, it will `close()` that socket. This means that our source is really only ever unicast, because the producer can only send to one observer. Here is a basic JSBin illustrating the idea.

Hot Observables: Producers created *outside*

  1. shares a reference to a producer
  2. starts listening to the producer
  3. multicast (usually²)

If we were to take our example above and move the creation of the WebSocket outside of our observable it would become “hot”:

const socket = new WebSocket('ws://someurl');const source = new Observable((observer) => {
socket.addEventListener('message', (e) => observer.next(e));
});

Now anything that subscribes to `source` will share the same WebSocket instance. It will effectively multicast to all subscribers now. But we have a little problem: We’re no longer carrying the logic to teardown the socket with our observable. That means that things like errors and completions, as well as unsubscribe, will no longer close the socket for us. So what we really want is to make our “cold” observable “hot”. Here is a JSBin showing this basic concept.

Why Make A “Hot” Observable?

source.filter(x => x % 2 === 0)
.subscribe(x => console.log('even', x));
source.filter(x => x % 2 === 1)
.subscribe(x => console.log('odd', x));

Rx Subjects

  1. It’s an observable. It’s shaped like an observable, and has all the same operators.
  2. It’s an observer. It duck-types as an observer. When subscribed to as an observable, will emit any value you “next” into it as an observer.
  3. It multicasts. All observers passed to it via `subscribe()` are added to an internal observers list.
  4. When it’s done, it’s done. Subjects cannot be reused after they’re unsubscribed, completed or errored.
  5. It passes values through itself. To restate #2, really. If you `next` a value into it, it will come out of the observable side of itself.

An Rx Subject is called a “subject” for item #3 above. “Subjects” in the Gang of Four Observer-Pattern are classes with an `addObserver` method, generally. In this case, our `addObserver` method is `subscribe`. Here is a JSBin showing the basic behavior of an Rx Subject.

Making A Cold Observable Hot

function makeHot(cold) {
const subject = new Subject();
cold.subscribe(subject);
return new Observable((observer) => subject.subscribe(observer));
}

Our new `makeHot` method will take any cold observable and make it hot by creating a subject that is shared by the resulting observable. Here’s a JSBin of this in action.

We still have a little problem, though, we’re not tracking our subscription to source, so how can we tear it down when we want to? We can add some reference counting to it to solve that:

function makeHotRefCounted(cold) {
const subject = new Subject();
const mainSub = cold.subscribe(subject);
let refs = 0;
return new Observable((observer) => {
refs++;
let sub = subject.subscribe(observer);
return () => {
refs--;
if (refs === 0) mainSub.unsubscribe();
sub.unsubscribe();
};
});
}

Now we have an observable that is hot, and when all subscriptions to it are ended, the `refs` we’re using to do reference counting will hit zero, and we’ll unsubscribe from our cold source observable. Here is a JSBin demonstrating this in action.

In RxJS, Use `publish()` or `share()`

In RxJS 5, the operator `share()` makes a hot, refCounted observable that can be retried on failure, or repeated on success. Because subjects cannot be reused once they’ve errored, completed or otherwise unsubscribed, the `share()` operator will recycle dead subjects to enable resubscription to the resulting observable.

Here is a JSBin demonstrating using `share()` to make a source hot in RxJS 5, and showing that it can be retried.

The “Warm” Observable

“Hot” And “Cold” Are All About The Producer

NOTES

² Hot observables are usually multicast, but they could be listening to a producer that only supports one listener at a time. The grounds for calling it “multicast” at that point are a little fuzzy.

Want to learn more? I run RxJS workshops at RxWorkshop.com!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store