An introduction to RxJS: things you need to know in 2022

Filip Guerrero-Mengana
Filip Guerrero-Mengana

Front-end developer

June 11, 2021

An introduction to RxJS: things you need to know in 2022

RxJS is a JavaScript implementation of a ReactiveX library that allows working with asynchronous, event-based code. ReactiveX (so as RxJS) is based on an observer pattern.

Pull vs. Push

RxJs is a push-based library, so it is important to understand the differences between push and pull systems for a complete understanding. The whole difference is about how data Producer & data Consumer interact. 

Pull-based behavior

Every JavaScript Function is a Pull system. When working with functions, first the call occurs, and then we receive some data from it. The code that calls the function is a Consumer, and the function is a data Producer.

In this case, the Consumer pulls data from the Producer, and it’s a Consumer who determines when it receives the data. The Consumer is an active element, while the Producer remains dormant until the data is pulled from it.

Push-based behavior

The most common example of a push system is Promise. Promise (data Producer) delivers resolved value to the registered callback functions (data Consumers). Promise determines when the callback function will receive the data - in other words, it’s responsible for pushing the data. The callback function is inactive until it receives the data. An interaction between the RxJS Observable (Producer) and Observer (Consumer) is based on the same behavior.

Single- and multiple-value Producers

In a Pull system, a single-value Producer is a JavaScript function since it can only return a single value on invocation. In a Push system, a single-value Producer is represented by Promise: the resolved-value could only be sent once.

Multiple-value Producers are distinguished by their ability to return n-values over time, and they’re present in both systems. In a Pull system, a multiple-value Producer is a JavaScript Generator function. Unlike simple functions, a Generator function is capable of returning zero to (potentially) infinite values on iteration. But it is still controlled by Consumer since the generator method next() is called from the ‘external’ code, leading to resuming a Generator function execution. 

In the scope of a Push system, an example of a multiple-value Producer might be an Event Listener. User interaction with a ‘targeted’ DOM element leads to multiple calls of the callback function. But in contrast to the Generator function, data Consumer (callback function) remains passive while data Producer (events triggered by user’s interaction) decides when Consumer will receive data. 

To summarise: in Pull systems, the Consumer determines when it receives data from the data Producer, while in Push systems, the Producer determines when to send data to the Consumer.

Basic RxJS concepts

What is Stream?

A Stream is a sequence of values in some time interval. It could be a simple numbers sequence generated within 6 seconds, the events from various elements on a form, text messages in chat sent via WebSocket. Everything above is the examples of data, which will be collected over some timespan.RxJS Observables are made to make work with Streams much more convenient.

Observable

Observables are objects that can emit data over some time. You can think of it as a function that returns the data Stream to the Observers, synchronously or asynchronously. The number of data returned by a Stream varies from zero to infinite.

Observers & Subscriptions

For Observable to work, it needs the Observer and a subscription. An Observer is a consumer of values delivered by an Observable. 

The Subscription serves as a link between the Observable and the Observer: the Observer connects to the Observable via the subscribe() method. 

Observable lifecycle

While working with Observers & subscriptions, the Observable goes through four stages of its lifecycle:

  1. Creation
  2. Subscription
  3. Execution
  4. Disposal

Creating Observable

Be default the Observable is created in the following way: 


The Observable constructor requires only one argument: a subscriber function.

When we mention the Observable execution, we talk about the execution of a code inside the subscriber function. To invoke the Observable we need the Observer and a subscription. 

We subscribe to the Observable by using the subscribe() method: Now that the Observable has gained a subscriber, the following result will be printed in the console: ‘Hello World!’.

Note that every subscription leads to the separate execution of the Observable. Multiple Observers that subscribed to the same Observable aren’t connected in any way.

Observable produces n-values (in sync or async manner) that get delivered to a subscriber during the execution. There are three types of values an Observable Execution can deliver:

  • "Next" notification: sends a value such as a Number, a String, an Object, etc. The number of “next” notifications isn’t limited.
  • "Error" notification: sends a JavaScript Error or exception.
  • "Complete" notification: does not send a value, serves as a signal that the Observable has completed successfully. You cannot pass values with that notification!

The "completed" and "error" states are final. That means, Observables cannot emit any data after delivering these notifications.

Giving the example: 

The console output would be: 

“I am number 1”

“I am number 2”

“I am number 3”  won’t be printed since the callback inside the subscribe method won’t send the data with the “complete” notification. And since the Observable won’t deliver anything after success/error notifications, the “I am number 4” also won’t be printed.

Cold vs. Hot Observables

In a nutshell:

  • If the data is generated by Observable itself, we call it a ‘cold’ Observable; 
  • If the data is generated outside the Observable, we call it ‘hot’.

Now, with more details:

Cold Observables

By default, Observables are lazy - the Observable execution only happens for each Observer that subscribes. The Observable starts a new execution for every subscriber, no data is shared between them. 

If the Observable generates multiple values, the situation when two subscribers receive different values can easily occur: 

In this case, the data generated inside the Observable (Math.random()), which means we’re dealing with the cold Observable. The subscribers are getting different values since the execution with the random number generation starts separately for every subscriber.

Hot Observables

In order to transform cold Observable into a hot one, we just need to move the data generation outside the Observable. Let’s slightly tweak a previous example: 

Now it’s a hot Observable since the data generation (Math.random()) occurs no matter if the Observable has the subscriber or not. The only difference is that by default, that data will be simply lost without the subscribers.

So, what’s better?

Depends on the use case. In general, you would want to keep the Observables cold, unless:

  • You need the ability to have n-subscribers that will receive the same data
  • You’re dealing with the new instance. For example, a WebSocket connection: you probably don’t want to create a separate connection for every new subscriber. Instead, you’d want to share a single connection between all of them.

Operators

Operators are what make RxJS useful. They allow you to manipulate the data from its source, returning an Observable with the modified data. 

There’s a ton of useful operators provided by RxJS out of the box, one for almost every use case.

How to use Operators

Most operators are known as Pipeable Operators since you can chain them with the pipe() method. Pipeable Operator is a pure function that takes one Observable as input and generates another Observable as output. They are located at ‘rxjs/operators’.

A pipe() method accepts Operators as params, and then calls then one by one:

Every subsequent Operator will receive a new Observable that was generated as a result of previous Operator execution.

Categories of Operators

RxJS Operators are split into categories which makes the search process much easier:

Combination Operators

They Allow combining the data from the multiple Observables. For instance, we can combine data updates from different sources to perform some calculations. Operator combineLatest() produces last values from each Observable whenever one of these produces a value: 

Creation Operators

These Operators can produce the Observable almost out of anything. For example, you can create a progress bar that will indicate the progress of reading the article. You can use the fromEvent() Operator to achieve that goal. It allows to create of an Observable based on the event data:

Error Handling Operators

This group allows us to handle errors efficiently and repeat the query if needed. The most used operator is catchError (you have to return the Observable when using it!):

Filtering Operators

Operators from this group are responsible for data stream filtering, and they also help when you need to adjust accumulated values in the data stream. For example, the take operator generates only the specified number of values until it completes its execution. You can subscribe to a click-event and process only the first click:

Transformation Operators

These Operators provide us with the method to transform the data. Like the scan operator that accumulates the state in a way like it works in Redux:

Subjects

Another big concept in RxJS.

Subject is a special type of Observable. It allows values to be multicasted to many Observers, which can subscribe to the Subject even when its execution has already started (on the contrary, the execution of a default Observable is unique for every subscriber). Let’s take a look at the example: 

The following result will be printed in the console:

   1st: 3

   1st: 9

   2nd: 9

A Subject is created using the new Subject() constructor.

Same as with the Observable, Observers are subscribed to it via the subscribe() method, which receives from Subject values of three types: next, error, and complete.

Internally to the Subject, subscribe() method does not invoke a new execution that delivers values. It simply registers the given Observer in a list of Observers.

Aside from the “standard” Subject, there are three varieties of it:

BehaviorSubject

BehaviorSubject stores the last emitted value. So every newly subscribed Observer will receive the “current value” immediately.

The initial value is set during the BehaviorSubject initialisation: 

The following result will be printed in the console:

 1st: 5

 2nd: 5 - if it was a “default” Subject, a second subscriber wouldn’t receive the last value, only the next one

 1st: 7

 2nd: 7

ReplaySubject

Unlike BehaviorSubject, a ReplaySubject object can store a given amount of the lastly emitted values. That amount is set during the object creation process.

Every Observer will receive the n-amount of “replayed” values of a ReplaySubject: 

The following result will be printed in the console:

1st: 5

1st: 6

1st: 7

2nd: 6

2nd: 7

AsyncSubject

With the AsyncSubject the Observers will receive only the last emitted value and only when it will complete its execution: 

In 3 seconds, the following result will be printed in the console:

Async: 9

Observable vs. Promise

Here are the key differences between Promise and Observable:

Callback-function execution

Callback-function that we send to a Promise constructor gets executed immediately: 

The console output:  

‘Callback call’

‘Before calling then…’

"Greeting from Promise: A-a-and resolved!"

In the case of cold Observable, a callback function will be executed only after calling a subscribe() method: 

The following result will be printed in the console:

“Before calling subscribe…”

“Callback call”

“Next!”

“Complete the Observable”

Asynchrony

Promise is always asynchronous, even if it resolves instantly:

The console output: 

"Before calling then…"

"After calling then..."

"Greeting from Promise: Promise Resolved!"

The message from then() method will be the last one displayed even though Promise was resolved without delay.

On the other hand, the Observable can be synchronous: 

Console output: 

"Before calling subscribe..."

"Callback call"

"Next!"

"Complete the Observable"

"After calling subscribe…"

And it can be asynchronous: 

Console output: 

"Before calling subscribe..."

"Callback call"

"After calling subscribe…"

"Next!"

"Complete the Observable"

Observables are collections of multiple values

A Promise can return only one value. It could be an array, but it’s a single object still. An Observable can emit n-values during its execution.

Operators

The aforementioned RxJS feature allows users to manipulate and modify the Observable data stream. Promise objects don’t have anything like that.

Memory Leak & Unsubscribe

Many Observables that we are subscribing to are potentially infinite (such as click-events) - we can’t tell beforehand how many values will be emitted. This means that we have to manipulate our Observable subscription on our own.

Why does memory leak occur?

Let’s take a look at the Angular component for reference: 

In this example, we’re using the RxJS function - timer. It creates an Observable that emits numbers. In our case - every second. 

The problem is, after the component will be unmounted, our subscription will live on, and it’ll keep printing the outputs in the console. And if the app reinitializes the same component, we’ll end up having another subscription, and so on and so on. An example of the memory leak is the components recreation without cleaning up our subscriptions.

How to manage the subscriptions to avoid memory leak

The unsubscribe() method:

An Observable subscribe() method returns a Subscription object. It has a method called unsubscribe(), which used to remove the subscription: 

A takeUntil() Operator:

The takeUntil() Operator allows us to use a declarative approach while working with data streams: 

A takeUntil(notifier: Observable<any>) Operator emits values produced by Observable until a notifier Observable produces values.

This is a declarative approach since we’re declaring our Observable-chain along with everything it needs for a complete lifecycle.

A take() and first() Operators:

Sometimes we need a subscription to work only once - for instance, when we’re loading some profile data at the application initialization.

In this case, it’s better to use either first() Operator (which emits only the first value), either take() (which emits only n-values), both unsubscribe automatically: 

In this example, the console output will display 0, 1, and 2 values. Afterward, the take() Operator will unsubscribe from the Observable.

Notice: take(1) and first() Operators won’t unsubscribe from the Observable before the first value is emitted. Use them only when you’re certain that at least one value will be produced. Otherwise, the subscription will remain active, which may lead to unexpected behavior.

Afterword

This article is meant to give you an overview of what RxJS is about without going too deep into each concept. Hopefully, it’ll add more clarity on the subject, but I definitely recommend taking a look at the official RxJS documentation for a deep dive into the topic.

Contact us
Akveo's case

Retool Dashboards with HubSpot Integration

Our client needed a centralized tool to aggregate account and contact activity, improving visibility and decision-making for the sales team.

The solution
We built a Retool application integrated with HubSpot, QuickMail, and Clay.com. The app features dashboards for sorting, filtering, and detailed views of companies, contacts, and deals, along with real-time notifications and bidirectional data syncing.

The result

  • MVP in 50 hours: Delivered a functional application in just 50 hours.
  • Smarter decisions: Enabled data-driven insights for strategic planning.
  • Streamlined operations: Reduced manual tasks with automation and real-time updates.

Learn more about the case

See More
See Less
Akveo's case

Lead Generation Tool to Reduce Manual Work

Our client, Afore Capital, a venture capital firm focused on pre-seed investments, aimed to automate their lead generation processes but struggled with existing out-of-the-box solutions. To tackle this challenge, they sought assistance from our team of Akveo Retool experts.‍

The scope of work
The client needed a tailored solution to log and track inbound deals effectively. They required an application that could facilitate the addition, viewing, and editing of company and founder information, ensuring data integrity and preventing duplicates. Additionally, Afore Capital aimed to integrate external tools like PhantomBuster and LinkedIn to streamline data collection.

The result
By developing a custom Retool application, we streamlined the lead generation process, significantly reducing manual data entry. The application enabled employees to manage inbound deals efficiently while automated workflows for email parsing, notifications, and dynamic reporting enhanced operational efficiency. This allowed Afore Capital's team to focus more on building relationships with potential founders rather than on administrative tasks.

Learn more about the case

See More
See Less
Akveo's case

Retool CMS Application for EdTech Startup

Our client, CutTime, a leading fine arts education management platform, needed a scalable CMS application to improve vendor product management and user experience.

The scope of work
We developed a Retool application that allows vendors to easily upload and manage product listings, handle inventory, and set shipping options. The challenge was to integrate the app with the client’s system, enabling smooth authentication and product management for program directors.

The result
Our solution streamlined product management, reducing manual work for vendors, and significantly improving operational efficiency.

Learn more about the case

See More
See Less
Akveo's case

Building Reconciliation Tool for e-commerce company

Our client was in need of streamlining and simplifying its monthly accounting reconciliation process – preferably automatically. But with a lack of time and low budget for a custom build, development of a comprehensive software wasn’t in the picture. After going through the case and customer’s needs, we decided to implement Retool. And that was the right choice.

The scope of work

Our team developed a custom reconciliation tool designed specifically for the needs of high-volume transaction environments. It automated the processes and provided a comprehensive dashboard for monitoring discrepancies and anomalies in real-time.

The implementation of Retool significantly reduced manual effort, as well as fostered a more efficient and time-saving reconciliation process.

→ Learn more about the case

See More
See Less
Akveo's case

Creating Retool Mobile App for a Wine Seller

A leading spirits and wine seller in Europe required the development of an internal mobile app for private client managers and administrators. The project was supposed to be done in 1,5 months. Considering urgency and the scope of work, our developers decided to use Retool for swift and effective development.

The scope of work

Our developers built a mobile application tailored to the needs of the company's sales force: with a comprehensive overview of client interactions, facilitated order processing, and enabled access to sales history and performance metrics. It was user-friendly, with real-time updates, seamlessly integrated with existing customer databases. 

The result? Increase in productivity of the sales team and improved decision-making process. But most importantly, positive feedback from the customers themselves.

→ Learn more about the case

See More
See Less
Akveo's case

Developing PoC with Low Code for a Tour Operator

To efficiently gather, centralize, and manage data is a challenge for any tour operator. Our client was not an exception. The company was seeking to get an internal software that will source information from third-party APIs and automate the travel itinerary creation process. Preferably, cost- and user-friendly tool.

The scope of work

Our experts ensured the client that all the requirements could be covered by Retool. And just in 40 hours a new software was launched. The tool had a flexible and easy-to-use interface with user authentication and an access management system panel – all the company needed. At the end, Retool was considered the main tool to replace the existing system.

→ Learn more about the case

See More
See Less
Akveo's case

Testing New Generation of Lead Management Tool with Retool

Our client, a venture fund, had challenges with managing lead generation and client acquisition. As the company grew, it aimed to attract more clients and scale faster, as well as automate the processes to save time, improve efficiency and minimize human error. The idea was to craft an internal lead generation tool that will cover all the needs. We’ve agreed that Retool will be a perfect tool for this.

The scope of work

The project initially began as a proof of concept, but soon enough, with each new feature delivered, the company experienced increased engagement and value. 

We developed a web tool that integrates seamlessly with Phantombuster for data extraction and LinkedIn for social outreach. Now, the company has a platform that elevates the efficiency of their lead generation activities and provides deep insights into potential client bases.

→ Learn more about the case

See More
See Less
Akveo's case

Building an Advanced Admin Portal for Streamlined Operations

Confronted with the need for more sophisticated internal tools, an owner of IP Licensing marketplace turned to Retool to utilize its administrative functions. The primary goal was to construct an advanced admin portal that could support complex, multi-layered processes efficiently.

The scope of work

Our client needed help with updating filters and tables for its internal platform. In just 30 hours we've been able to update and create about 6 pages. Following features were introduced: add complex filtering and search, delete records, styling application with custom CSS. 

Together, we have increased performance on most heavy pages and fixed circular dependency issues.

→ Learn more about the case

See More
See Less
Akveo's case

Creating MVP Dashboard for Google Cloud Users

Facing the challenge of unoptimized cloud resource management, a technology firm working with Google Cloud users was looking for a solution to make its operations more efficient. The main idea of the project was to create an MVP for e-commerce shops to test some client hypotheses. Traditional cloud management tools fell short.

The scope of work

Determined to break through limitations, our team of developers turned Retool. We decided to craft an MVP Dashboard specifically for Google Cloud users. This wasn't just about bringing data into view; but about reshaping how teams interact with their cloud environment.

We designed a dashboard that turned complex cloud data into a clear, strategic asset  thanks to comprehensive analytics, tailored metrics, and an intuitive interface, that Retool provides. As the results, an increase in operational efficiency, significant improvement in cost management and resource optimization.

→ Learn more about the case

See More
See Less
Akveo's case

Elevating CRM with Custom HubSpot Sales Dashboard

Our other client, a SaaS startup, that offers collaborative tools for design and engineering teams, was on a quest to supercharge their sales efforts. Traditional CRM systems were limited and not customizable enough. The company sought a solution that could tailor HubSpot to their workflow and analytics needs.

The scope of work

Charged with the task of going beyond standard CRM functions, our team turned to Retool. We wanted to redefine how sales teams interact with their CRM. 

By integrating advanced analytics, custom metrics, and a user-friendly interface, our developers provided a solution that transformed data into a strategic asset.

In 40 hours, three informative dashboards were developed, containing the most sensitive data related to sales activities. These dashboards enable our customer to analyze sales and lead generation performance from a different perspective and establish the appropriate KPIs.

→ Learn more about the case

See More
See Less
Akveo's case

Retool for Sales and CRM Integration

See More
See Less
Akveo's case

Building a PDF Editor with Low-Code

Our client, a leading digital credential IT startup, needed a lot of internal processes to be optimized. But the experience with low-code tools wasn’t sufficient. That’s why the company decided to hire professionals. And our team of developers joined the project.

The scope of work

The client has a program that designs and prints custom badges for customers. The badges need to be “mail-merged” with a person’s info and turned into a PDF to print. But what is the best way to do it?

Our developers decided to use Retool as a core tool. Using custom components and JavaScript, we developed a program that reduced employees' time for designing, putting the data, verifying, and printing PDF badges in one application.

As a result, the new approach significantly reduces the time required by the internal team to organize all the necessary staff for the conference, including badge creation.

→ Learn more about the case

See More
See Less
Subscription
Subscribe via Email

Want to know which websites saw the most traffic growth in your industry? Not sure why your SEO strategy doesn’t work?

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

By clicking “Subscribe” you agree to Akveo Privacy Policy and consent to Akveo using your contact data for newsletter purposes

More articles by themes

Cross
Contact us
AnnaRodionEvgenyExpertExpertExpert
Cross
Got any questions?
Our domain expert is here to answer
If you have any questions, feel free to leave me a personal message on LinkedIn. We are here to help.
Thanks for your question
We will contact you soon
We have a problem
Please, check the entered data
Got any questions?