Author:
Reactive programming is a new approach to developing apps and is getting a lot of hype today. Learn how to level up your code to react to changes automatically and new ways to approach problems and process asynchronous data as streams.
Reactive programming may be an unfamiliar paradigm, but actually, it is nothing really new. Microsoft Excel was built on it long before it was popular.
In recent years, Reactive programming found its way into modern apps with things like state management in web frameworks and video game engine tools.
The focus of this article is the JavaScript implementation of ReactiveX (Rx.js), which is a library for composing asynchronous and event-based apps using Reactive programming.
But before we talk about Rx.js, we must learn a few things like Declarative, Functional, Reactive programming, Observer pattern, Observables, and how they all blend into one.
The first step towards writing Reactive code is to think differently about writing code. You may have heard these programming terms before:
Imperative programming is based on detailed steps that need to be done in order to perform an operation. Declarative is based only on what needs to be done.
Let's look at a couple of examples of the two. In all of these examples two snippets of code do the exact same thing, but in a different way:
Imperative approach
Declarative approach
Imperative approach
Declarative approach
Imperative approach
Declarative approach
It is clear that Declarative programming is not only shorter but also requires a different approach to the situation at hand. It's direct on What needs to be done rather than How.
The Declarative programming in the ReactiveX world closely resembles JavaScript higher-order functions (or C# LINQ) where each operation is a method chained to the previous.
ReactiveX Reactive programming is based on Observables, on which we'll elaborate further on.
The Declarative syntax allows us to focus on things one at a time in the right order:
There are only a few variables to remember as they appear in order (in the same line/pipeline). We can look up everything that needs to happen just by reading the code from top to bottom,
as opposed to having the logic scattered up and down through the file (Imperative approach).
Here, the state is been transferred from one operation to another, meaning you can easily trace it back to the original form.
Modifications are also welcome.
You can easily transform, schedule, or filter values within the pipe just by adding operators:
Rx.js is not only Declarative but also Reactive. It reacts to changes. To explain what this means we'll look into the Observer pattern next.
In software design and engineering, the Observer pattern
is a software design pattern in which an object, named the Subject, maintains a
list of its dependents,
called Observers, and notifies them automatically of any state changes,
usually by calling one of their methods.
-Wikipedia
In simple terms, you have one object, function, or component that is producing events and a list of subscribers to those events that are invoked (automatically) whenever a new event arrives.
Let's try to think about how Youtube works. You as a YoutubeUser can search and watch videos, comment, and like, but the interesting part is that you can (1) subscribe to some Youtube channels and you'll be (2) notified whenever a new video goes live.
On the YoutubeChannel side of things, a channel has a list of (1) subscribers, which is been (2) updated when a YoutubeUser subscribes to the channel. Each channel subscriber (from the list) gets notified when a new video is (3) uploaded.
Let's create some Youtube Channels:
And let's create users that will subscribe to these channels:
And what happens now? Well, nothing. Now we wait for one of the channels we subscribed to upload a new video:
When a new video goes up, subscribers get immediately notified:
If another video goes live from Master Splinter, his subscribers (Ninja Turtles) will be notified again:
In the Reactive programming context, the:
Its purpose is to bring the power of Reactive programming into your apps, by providing solutions for major programming languages:
Generated by MidJourney AI
At the heart of ReactiveX lie the Observables.
The data that is passed from Producer (Subject) to the Consumer/Receiver (Observer) is wrapped into an Observable. The Observable represents a sequence of events that occur in a never-ending interval.
An Observable where the producer is a radio chatter
That said you can unsubscribe from an Observable at any time. To extract data from an Observable (or should I say convert Observable<T> to plain <T>), you put a call to subscribe() function at the end. The subscribe() function returns a callback function that holds raw data as its first parameter.
The Observable pattern is closely related to the Observer pattern. The key difference is that in the Observer pattern, observers register with a Subject to receive updates, while in the Observable pattern, observers subscribe to an Observable to receive emitted values or events.
In order to effectively work with Observables, we need to fully understand how they behave:
What this means is that without the subscribe() call at the end, the Observable will never be activated, it will never create an API call or send data to consumers.
The Observable can work both in a synchronous and asynchronous manner depending on the producer. An observable created from a primitive value (like a string) is no doubt synchronous, while an Observable produced by an API or a timer is asynchronous. In addition to that, Observables can also be converted from one type to another using Schedulers.
Because observables work on callbacks.
This can be achieved in a number of ways, either using a subscription:
Or reactively after an Observable is called several times:
If we compare this to a JavaScript Promise, we can immediately see that Promises:
And you can see how much ground an Observable covers.
Photo by Helena Jankovičová Kováčová from Pexels
Let's give Observables a second look.
The difference between the two is how they share data with existing and late subscribers. In the former, the Observable is invoked whenever a new subscriber joins the club (subscribes to the Observable).
With Hot Observables early subscribers get the data, while late ones do not get anything
until Observable emits again, in which case both early and late get the same (latest) data.
This of course can vary depending on the type of producer (Subject).
The Observables are Cold by nature but can be converted to Hot using Subjects or a set of operators (e.g. share()).
While in the next state, the Observable can continuously emit data,
hence why we referred to it previously as a never-ending stream.
However, in case of an unhandled error, the Observable will stop emitting data.
There is also a completed state in which Observable is considered as successfully
finished
and can no longer emit data.
In web frameworks, it's recommended to unsubscribe from any active observables in components that are no longer been used to avoid memory leaks.
The name of the variable that holds the Observable usually ends with a $ sign, e.g. data$.
To create a new Observable we use the Observable instance that returns an Observer as a parameter of a callback function.
There are shorthand alternatives to create Observables like of() or from().
In between the source Observable and the subscribe() function you can create a pipe function that holds operators that will transform, filter, destroy (unsubscribe), schedule, or combine Observables.
The subscribe function also returns an Observer object that can track changes.
Like before there is a shorthand version for subscribe function.
In your typical JavaScript application, there is a distinct difference in how you handle synchronous or asynchronous data.
Rx.js however, uses the same API to do both. Looking at the code below it's hard to tell if the code is asynchronous or not.
This is synchronous, while the example below is asynchronous:
Even events that are synchronous by nature (like a string text) can be converted to asynchronous using schedulers, delays, or by calling an API in the next step based on a string provided in the producer (source value).
The bottom line, when working with Rx.js Observable streams there is no difference in processing data that is synchronous compared to one that is not.
Now let's look at the Pros and Cons and when you'd want to use Rx.js:
There are a number of ways to include Rx.js in your existing project:
I recently published a blog explaining how to use NPM modules in web applications without frameworks and here I set up Rx.js into a vanilla JS/TS web app (without CDN). If you're interested here is a full guide and a repository.
To summarize, Reactive programming describes a paradigm that relies on
asynchronous programming logic to trigger changes in real-time.
The logic behind it lies in the Observer pattern.
Producers of the events are called Subjects who sent data wrapped in
an Observable to Observers that act upon it. The code itself is written in a
Declarative manner where each step is an operator that can manipulate the data.
All in all, Reactive programming, Observables, and ReactiveX are vast topics. Hopefully, in this article, you were able to acknowledge the possibilities of this programming style.
Bye for now!
Author: