Mastering Subjects in Rxjs

Kinanee Samson - Mar 15 '21 - - Dev Community

In this article we are going to be talking about Rxjs subjects, this is a special type of Observable that can allow us to simultaneously emit a value and subscribe it.
A Subject allows us to multicast to different observers, this is another advantage of using Subjects. A normal Observable cannot act like this because each observer maintains it's own implementation of an Observable, thus when two different observers subscribe to the Observable, they both receive different values.

import { Observable } from 'rxjs'

const observable = new Observable(subscriber => {
subscriber.next(Math.random())
})

observable.subscribe(x => console.log(x))
observable.subscribe(x => console.log(x))
Enter fullscreen mode Exit fullscreen mode

The above example is the typical behavior of Observables, but subjects behave differently. Let's see somethings about Subjects.

  1. Subjects are Observables. Given a Subject, we can call the Observable.next() method on it to emit values which is native to Observables. Thus we can provide an observer that will subscribe to it. Rather than create a new Observable for that subscriber, it will add the observer to it's internal list of observers.
  2. Subjects are also observers and thus we can use them to consume other Observables, it has the following methods next(), error() and complete(). It will act like a a proxy, i.e multicast to other observers registered on that subject. Multicasting simply refers to a source of data that has multiple receivers. Let's create a Subject.
import { Subject } from 'rxjs'

const subject = new Subject<number>()

subject.subscribe (v => console.log(v))
subject.subscribe (v => console.log(v))

subject.next(Math.floor(Math.random() * 3))
Enter fullscreen mode Exit fullscreen mode

Earlier we had discussed this; that a subject is able to emit data and later that subject will still consume the data later. The example above just showed us that. We created a subject, we subscribed to the subject, providing two separate observers. Later we used the .next() method to emit data, you will find out that the two observers got the same value. This is because Subjects multicast their values to a list of observers, the two observers we provided above were added to the Subject's list of observers and once the data was available the subject just passed out the copy of the data each observer needs from the same Observable.

import  { of, Subject } from 'rxjs'

const subject = new Subject<string>()

subject.subscribe(x => console.log('first observable', x))
subject.subscribe(x => console.log('second observable', x))

const heroes = ['thor', 'hulk', 'ironman']
const Heroes$ = of(...heroes)

Heroes$.subscribe(subject)
// first observer thor
// second observer thor
// first observer hulk
// second observer hulk
// first observer ironman
// second observer ironman
Enter fullscreen mode Exit fullscreen mode

In this example we have also shown how we can use Subjects as observers, we created a subject, then created two observers for that subject. Then provided that subject as an observer to a Heroes$ observable we created with the of() operator. And we still get the multicasting too. Subjects allow us to create hot observables through multicasting. Essentially any Observable that is a multicast is a hot observable, while unicast observables are cold observables.
There are also variants of subjects, there is
BehaviorSubject, ReplaySubject, AsyncSubject.

BehaviorSubject

This is a special Observable that pushes only the current value emitted to an observer or a list of observers, although observers that are declared after a value is emitted might still get that value, however it will only get the most recent value not the entirety.

import { BehaviorSubject } from 'rxjs'

const Heroes$ = new BehaviourSubject('hawkeye')

Heroes$.subscribe(x => console.log(`first observer ${x}`))

Heroes$.next('captain America')
Heroes$.next('black widow')

Heroes$.subscribe(x => console.log(`second observer ${x}`))

Heroes$.next('deadpool')
Heroes$.next('logan')

// first observer hawkeye
// first observer captain America
// first observer black widow
// second observer black widow
// first observer deadpool
// second observer logan

Enter fullscreen mode Exit fullscreen mode

We can use a behaviorSubject to hold a scoreline for a football match.

import { BehaviorSubject } from 'rxjs'

const Scoreline$ = new BehaviorSubject('0 - 0')

Scoreline$.subscribe(x => console.log(`DCU ${x} MCU`)

$Scoreline.next('1 - 0')
$Scoreline.next('1 - 1')

Scoreline$.subscribe(x => console.log(`HT DCU ${x} MCU`)

// DCU 0 - 0 MCU
// DCU 1 - 0 MCU
// DCU 1 - 1 MCU
// HT DCU 1 - 1 MCU
Enter fullscreen mode Exit fullscreen mode

ReplaySubject

A ReplaySubject is quite similar to a BehaviorSubject, however a ReplaySubject will keep a record of values that has been emitted to an observer. We pass in an argument that represents how long we want the record to be, another that represents the number of milliseconds we want to store that record.

import { ReplaySubject } from 'rxjs'

const Subject$ = new ReplaySubject(3)

Subject$.subscribe(x => console.log(`observer a ${x}`))

Subject$.next(1)
Subject$.next(2)
Subject$.next(3)
Subject$.next(4)

Subject$.subscribe(x => console.log(`observer b ${x}`))

// observer a 1
// observer a 2
// observer a 3
// observer a 4
// observer b 3
// observer b 4
Enter fullscreen mode Exit fullscreen mode

AsyncSubject

This is a special type of Observable that emits only its last value, after the Observable is done executing.

import { AsyncSubject } from 'rxjs'
const subject = new AsyncSubject()

subject.subscribe(x => console.log(`observer a: ${x}`))

subject.next(1)
subject.next(2)
subject.next(3)

subject.subscribe(x => console.log(`observer b: ${x}`))

subject.next(4)
subject.complete()

// observer a 4
// observer b 4
Enter fullscreen mode Exit fullscreen mode

Thats all for now, we'll look more closely at Operators next, i do hope that you find this useful.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .