RxJS Simplified - Subjects (marble party!)
π€π₯ Jasper de Jager
Posted on March 27, 2021
First: WOW
Thanks for all the interest in the RXJS Simplified article! I'd like to repay this enthusiasm with some enthusiasm of my own so I started writing an open source Angular app to create marble machines using RXJS so stay tuned!π
For now let's continue learning RXJS and see what kind of Subjects (marble machines) RXJS has to offer. We'll use the analogy of a marble party for this one.
Subject
This is the standard Subject. It's Job is to accept data to emit and to emit it to all its observers. At the marble party: You are able to insert new marbles into the machine and these marbles will be emitted to all people at the party (observers). Two things I didn't mention in the first post:
First: The machine can also be turned off. This is done by calling the complete function on the Subject. You now have a closed Subject.
Second: As with any party people can leave early, this means they stop observing the marble machine (unsubscribe).
import { Subject } from "rxjs";
// create the partyMachine
const partyMachine = new Subject();
// Jonah arrived at the party!
const subscriptionJonah = partyMachine.subscribe(
(marble) => console.log("Jonah", marble)
)
// insert a red marble in the machine
partyMachine.next('red')
// Output:
// Jonah, red
// Charlotte also arrives! better late than never
const subscriptionCharlotte = partyMachine.subscribe(
(marble) => console.log("Charlotte", marble)
)
// insert a green marble in the machine
partyMachine.next('green')
// Output:
// Jonah, green
// Charlotte, green
// Jonah has had enough, he leaves
subscriptionJonah.unsubscribe();
// insert a pink marble in the machine
partyMachine.next('pink')
// Output:
// Charlotte, pink
//Party's over! stop the machine
partyMachine.complete();
// Charlotte was automatically sent home! (unsubscribed)
// Here come's Anna, too late as always...
const subscriptionAnna = partyMachine.subscribe(
(marble) => console.log("Anna", marble)
)
// The machine is off (Subject closed) so Anna gets sent
// home (unsubscribed) immediately
ReplaySubject
As you can see in the previous example if you're late to the party you wonβt get any marbles (data) until new ones are added to the machine (Subject). There is a solution for this, the ReplaySubject. The ReplaySubject as marble machine: The marble machine keeps track of the last marbles (data) it emitted for those who come late and emits them immediately when they join the party (subscribe). The indented outputs mark the differences with the example of the "normal" Subject.
import { ReplaySubject } from "rxjs";
// create the partyMachine with a memory of 2 marbles
const partyMachine = new ReplaySubject(2);
// Jonah arrived at the party!
const subscriptionJonah = partyMachine.subscribe(
(marble) => console.log("Jonah", marble)
)
// insert a red marble in the machine
partyMachine.next('red')
// Output:
// Jonah, red
// Charlotte also arrives! better late than never
const subscriptionCharlotte = partyMachine.subscribe(
(marble) => console.log("Charlotte", marble)
)
// Ouput:
// Charlotte, red
// insert a green marble in the machine
partyMachine.next('green')
// Output:
// Jonah, green
// Charlotte, green
// Jonah has had enough, he leaves
subscriptionJonah.unsubscribe();
// insert a pink marble in the machine
partyMachine.next('pink')
// Output:
// Charlotte, pink
//Party's over! stop the machine
partyMachine.complete();
// Charlotte was automatically sent home! (unsubscribed)
// Here come's Anna, too late as always...
const subscriptionAnna = partyMachine.subscribe(
(marble) => console.log("Anna", marble)
)
// The machine is off (Subject closed) but still remembers
// the last two marbles
// Output:
// Anna, green
// Anna, pink
// Anna gets sent
// home (unsubscribed)
A ReplaySubject can remember all marbles
new ReplaySubject();
or just the last N marbles
new ReplaySubject(N)
BehaviorSubject
Sometimes it is easy to get information from outside of the party, this is where the BehaviorSubject comes in. The BehaviorSubject always has a value so it must be initialized with the first value to emit. It also has a getValue method so you can get the last emitted value without having to subscribe. Back to the party: The machine has a way of returning the last emitted marble without having to observe it. Let's say you are now able to call the machine to get the last emitted marble. It will also emit the last marble if you join the party (subscribe).
import { BehaviorSubject} from "rxjs";
// create the partyMachine with an initial value
const partyMachine = new BehaviorSubject('blue');
// Jonah arrived at the party!
const subscriptionJonah = partyMachine.subscribe(
(marble) => console.log("Jonah", marble)
)
// Output:
// Jonah, blue
// insert a red marble in the machine
partyMachine.next('red')
// Output:
// Jonah, red
// Mom calls to ask the last emitted marble
console.log("Mom: last marble?", partyMachine.getValue())
// Output
// Mom: last marble?, red
// Charlotte also arrives! better late than never
const subscriptionCharlotte = partyMachine.subscribe(
(marble) => console.log("Charlotte", marble)
)
// Ouput:
// Charlotte, red
// insert a green marble in the machine
partyMachine.next('green')
// Output:
// Jonah, green
// Charlotte, green
// Jonah has had enough, he leaves
subscriptionJonah.unsubscribe();
// insert a pink marble in the machine
partyMachine.next('pink')
// Output:
// Charlotte, pink
//Party's over! stop the machine
partyMachine.complete();
// Charlotte was automatically sent home! (unsubscribed)
// Here come's Anna, too late as always...
const subscriptionAnna = partyMachine.subscribe(
(marble) => console.log("Anna", marble)
)
// The machine is off (Subject closed) so Anna gets sent
// home (unsubscribed) immediately
// Mom calls to ask the last emitted marble
console.log("Mom: last marble?", partyMachine.getValue())
// Output
// Mom: last marble?, pink
What may be unexpected is that the BehaviorSubject doesn't emit the last marble when you subscribe when it is closed (I didn't know this until I made this example and I use RXJS a lot!). Also not that when you use getValue you just get the value, you are not subscribed. Because of this my advice is to only use the BehaviorSubject when you have no other choice because the use of getValue is not reactive programming (you are not at the party and cannot observe the marble machine).
AsyncSubject
The AsyncSubject only emits when it is completed and only emits the last data you gave it. As a marble machine: You can feed all the marbles you want but when you shut it down the observers only get the last marble you put in. I have no idea how this can add to any party so if you know a good example please let me know!
import { AsyncSubject } from "rxjs";
// create the partyMachine
const partyMachine = new AsyncSubject();
// Jonah arrived at the party!
const subscriptionJonah = partyMachine.subscribe(
(marble) => console.log("Jonah", marble)
)
// insert a red marble in the machine
partyMachine.next('red')
// Charlotte also arrives! better late than never
const subscriptionCharlotte = partyMachine.subscribe(
(marble) => console.log("Charlotte", marble)
)
// Jonah has had enough, he leaves
subscriptionJonah.unsubscribe();
// insert a pink marble in the machine
partyMachine.next('pink')
//Party's over! stop the machine
partyMachine.complete();
// Output:
// Charlotte, pink
// Charlotte was automatically sent home! (unsubscribed)
// Here come's Anna, too late as always...
const subscriptionAnna = partyMachine.subscribe(
(marble) => console.log("Anna", marble)
)
// Output:
// Anna, pink
// The machine is off (Subject closed) so Anna gets sent
// home (unsubscribed) immediately
Personally I have never used this type of Subject before, never had a case for it, but that doesn't mean you should just forget about it! ( I can think of some cases actually but I'll get back on this Subject when we dive even deeper and play with Pipes/Operators in the next post of this series).
That's all
That are all Subjects RXJS offers. In the next post I'm going to explain Pipes and Operators with an analogy of marble party games!
Posted on March 27, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.