RxJS is a framework for reactive programming that makes use of observables, which makes it really easy to write asynchronous code.
According to the official documentation, this project is a kind of reactive extension to JavaScript with “better performance, better modularity, better debuggable call stacks, while staying mostly backwards compatible, with some breaking changes that reduce the API surface.”
It is the official library used by Angular to handle reactivity, converting pull operations for callbacks into observables.
To be able to follow this article’s demonstration, you should have:
Confirm that you are using version 7 using the command below, and update to 7 if you are not.
// run the command in a terminal ng version
Download this tutorial’s starter project here to follow through the demonstrations. Unzip the project and initialize the Node modules in your terminal with this command:
npm install
RxJS subjects are observables that also act as observers and provide a platform for data values to be multicasted to more than one observer. An observable can be defined simply as a function that returns a stream of data values to one observer over time.
A subject is a kind of advanced observable that returns values to more than one observer, which allows it to act as a kind of event emitter.
First of all, it is an observable, so all the methods available for use with observables automatically work with subjects. The additional fact that you can multicast, which means that more than one observer can be set for a subject, is really awesome.
Observables act purely as producers, but subjects can be both producers and consumers, shifting the reach of observables from unicast to multicast. Subjects should be used in place of observables when your subscriptions have to receive different data values. With multicasting, it matches each subscription to its respective observer.
Inside an Angular project, the syntax for defining an RxJS subject looks like this:
import { Subject } from "rxjs"; ngOnInit(){ const subject = new Subject(); }
To illustrate RxJS subjects, let us see a few examples of multicasting. If you started reading this post from the start, you will have the starter project open in your VS Code application. Open your app.component.ts
file and copy the code below into it:
import { Component, OnInit } from '@angular/core'; import { Subject } from "rxjs"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ ngOnInit(){ const subject = new Subject(); subject.subscribe({ next: (data) => console.log('First observer prints '+ data) }); subject.next(1); subject.next(2); } }
You’ll see that unlike the observable, which requires a kind of helper module passed to create it, the subject just needs a new subject construct, and with that, you can go ahead and use it just as you would any observable. If you run the app in development with the dev command:
ng serve
You will see that it logs data values just as we’d expect, emulating a fully functional observable. This means that both the error and the complete values can be passed to the observer.
Remember that one of subjects’ main features is their ability to have more than one observer set that can make reference to it. You will see that in action with the same logic as we have above. Copy the code block below into the app.component.ts
file:
import { Component, OnInit } from '@angular/core'; import { Subject } from "rxjs"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ ngOnInit(){ const subject = new Subject(); subject.subscribe({ next: (data) => console.log('First observer prints '+ data) }); subject.next(1); subject.subscribe({ next: (data) => console.log('Second observer prints '+ data) }); subject.next(2); subject.next(3); } }
If you save the file and it recompiles, you will notice that although there are two observers for the subject, the various observers still return data values as expected.
If you notice, the second observer did not receive the very first next
value because the subject simultaneously holds and efficiently distributes the values according to scope and definition. This is the beauty of using subjects in RxJS.
There are officially three variants of RxJS subjects. They are:
The behavior subject is a very special type of subject that temporarily stores the current data value of any observer declared before it. Here is a clear illustration — copy the code below into your app component file:
import { Component, OnInit } from '@angular/core'; import { BehaviorSubject } from "rxjs"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ ngOnInit(){ const subject = new BehaviorSubject(0); subject.subscribe({ next: (data) => console.log('First observer prints '+ data) }); subject.next(1); subject.next(2); subject.subscribe({ next: (data) => console.log('Second observer prints '+ data) }); subject.next(3); } }
Here you see that the behavior subject is imported from RxJS, and the new construct must take in an initial value (which was zero in our case). Also, unlike the prior illustration, you see that the very last data value before the new observer was called (the current value 2
) was stored and then reported by the new observer even though it was defined after the reference to it.
This is exactly what the behavior subject achieves: storing and then passing on the current data value to the new observer.
After viewing the possibilities that comes with the behavior subject variant, any curious person might ask why they can’t store more than the current value. Well, the good news is that with the replay subject, you can. So the replay subject is basically the behavior subject with the option to choose how many values you want to emit from the last observer. Here is a quick example:
import { Component, OnInit } from '@angular/core'; import { ReplaySubject } from "rxjs"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ ngOnInit(){ const subject = new ReplaySubject(2); subject.subscribe({ next: (data) => console.log('First observer prints '+ data) }); subject.next(1); subject.next(2); subject.subscribe({ next: (data) => console.log('Second observer prints '+ data) }); subject.next(3); } }
Here it is specified that only one last value be emitted from the last observer, so the output in your browser console should be exactly the same save for the initial log line.
Additionally, this replay subject can take an optional second argument called window time, recorded in milliseconds. It just allows you to time the return.
This is the very last variation. It acts exactly the same as the behavior subject but can only execute after a complete method is called. Remember there are three methods that an observable can call: next, error, and complete. So this particular variation emits the very current value only when it sees the complete method call.
import { Component, OnInit } from '@angular/core'; import { AsyncSubject } from "rxjs"; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ ngOnInit(){ const subject = new AsyncSubject(); subject.subscribe({ next: (data) => console.log('First observer prints '+ data) }); subject.next(1); subject.next(2); subject.subscribe({ next: (data) => console.log('Second observer prints '+ data) }); subject.next(3); subject.complete(); } }
If you run this in your development server, your browser console will look like this:
This is an introductory overview of subjects in RxJS and how important they are in your workflow. There were also illustrations and even explanations of the three variations that subjects come in. Now you can start to use them in your Angular projects — happy hacking!
Debugging Angular applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Angular state and actions for all of your users in production, try LogRocket. https://logrocket.com/signup/
LogRocket is like a DVR for web apps, recording literally everything that happens on your site including network requests, JavaScript errors, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket NgRx plugin logs Angular state and actions to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.
Modernize how you debug your Angular apps – Start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowExplore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.
Optimize search parameter handling in React and Next.js with nuqs for SEO-friendly, shareable URLs and a better user experience.