Skip to main content

2 posts tagged with "reactive programming"

View All Tags

· 7 min read
Valentin Richard

In this post I want to introduce you to Reactive Programming, it's benefits, and why should you care.

map-pipe Credit: Dan Lew1

Even developers already using Reactive Programming Frameworks (ex: Angular) struggle with this concept. It is something mysterious, and complex, often limiting its usage. However, coding in RP gives very powerful powers, that are clearly underestimated. So today, I will try to clear up this mystery.

Reactive programming

Let's begin with a simple definition:

Reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change.

In Reactive Programming, we will focus on the data flow, and if something changes, then the flow updates itself.

I'll illustrate this with a switch and a light bulb:

proactive-switch-light Credit: Dan Lew1

In imperative programming (this is probably your main way of coding), the switch modifies the state of the bulb. In this case, the switch is proactive, pushing new states to the bulb; whereas the bulb is passive, simply receiving commands to change its state.

However, in reactive programming it's the opposite: the bulb listen to the state of the switch, and then modify itself accordingly. In this model, the bulb is reactive, changing its state based on the switch's state; whereas the switch is observable, in that others can observe its state changes.

reactive-switch-light Credit: Dan Lew1

This approach makes more sense when working with streams and flows, because you won't have to update the source (the switch) to add more sinks (more light bulbs). Each light bulb is self-managed, and doesn't rely on a modification of the code of the switch to update itself.

Maybe you've seen it, but you're already familiar with this concept: everytime you use the method addEventListener, you're subscribing to Events and update other variables, elements, etc... So, you're actually doing reactive programming. Here, we'll push this concept further, to create amazing possibilities of interactions between data, objects, sources, etc...

Imperative programming

Now, we will compare imperative vs reactive programming 2.

Let's explain it with a simple assignment: a = b + c. a is being assigned the result of b + c. This is done in the instant in imperative programming: the expression is evaluated, and later, the values of b and c can be changed with no effect on the value of a. This is how we code most of the time.

On the other hand, in reactive programming, the value of a is automatically updated whenever the values of b or c change, without the program having to explicitly re-execute the statement a = b + c to determine the presently assigned value of a.

Imperative programming

let b = 1;
let c = 2;
let a = b + c;
b = 10;
console.log(a) // 3 (not 12 because "=" is not a reactive assignment operator)

Reactive programming

Now imagine you have a special operator $= that changes the value of a variable (executes code on the right side of the operator and assigns result to left side variable) not only when explicitly initialized, but also when referenced variables (on the right side of the operator) are changed.

let b = 1;
let c = 2;
let a $= b + c; // 3
b = 10;
console.log(a); // 12

Such an operator would be extremely useful: a would be always up-to-date. We may start to imagine new langues and frameworks using this paradigm, and having quite powerful features. This is especially true for MVC architectures, where reactive programming can facilitate changes in an underlying model that are reflected automatically in an associated view (typically an UI framework like Angular of React for a web application).

@lirx/core as a framework

The most popular reactive framework is ReactiveX, and it's javascript implementation RxJS. However, this library has the following issues:

  • it lacks of clear documentation (you need a master in reactive programming, to understand even the simplest functions), and some parts of the documentation are outdated, or simply don't exist.
  • it is based on states ('next', 'error' and 'complete') instead of pure data flow, which restrains many operators, and confuse users when building complex interactions.
  • its design limits its performance as well as the resulting bundle size.

Instead, I'll use @lirx/core, and show you how simple is it to implement the previous example:

const [$b, b$] = let$$(1);
const [$c, c$] = let$$(2);
const a$ = add$$(b$, c$);

a$(a => console.log(a)); // 3

$b(10); // a$ => 12

I will break down the code, to explain each part in detail:

const [$b, b$] = let$$(1);

The let$$ function allows us to create a mutable reactive variable. We usually split the result in two constants:

Moreover, as you probably guess, we've initialed b with the value 1.

We repeat the process for c, and have now two reactive variables.

Then we have to perform the sum:

const a$ = add$$(b$, c$); 

Here we declare a$ as the addition of b$ and c$, using the function add$$. All of these variables are Observables. In consequence, if any of b$ or c$ changes, then a$ changes too.

Since a$ is an Observable, we have to subscribe to it in order to read its value (because this one changes over time):

a$(a => console.log(a));

b and c already have a value, as a result, we'll immediately see 3 logged.

Finally, if we update b writing:

$b(10); 

a$ will instantly change to 12, and this value will be logged.

Conclusion

This is just the tip of the iceberg. As you probably guess, adopting a reactive programming approach gives you very powerful tools to design clear and organized data flows.

A stream can be used as an input to another one. Even multiple streams can be used as inputs to another stream. You can merge two streams. You can filter a stream to get another one that has only those events you are interested in. You can map data values from one stream to another new one. Etc.

This way you'll be able to create well-defined and robust streams of events and data, and you'll have the tools to create flow that match perfectly and instinctively the requirements of stable and large applications perfectly fitting your needs.

I highly recommend you to stop thinking purely in imperative programming and start practicing reactive programming to discover an amazing world of new possibilities.

I guarantee you won't regret it 🦸

About @lirx/core

@lirx/core is a Reactive Programming framework with a lot of prebuild functions, clear documentations and many examples to teach you how to become a master of RP. It allows you to develop complex applications and pipelines that scale easily. Moreover, it is the fastest and smallest javascript library for Reactive Programming. So it's a good candidate to start your journey with Observables.

Feel free to test this library, share it and give your feedbacks.

Footnotes

  1. Dan Lew - An Introduction to Functional Reactive Programming 2 3

  2. Wikipedia - Reactive programming

· 9 min read
Valentin Richard

In this post I want to share my personal experience with Reactive Programming and why you should consider using it.

rp meme

The problematic

Let's imagine this simple use case:

Two <input> in which the user may enter two numbers to perform an addition. The result is displayed right below everytime the user type a new value.

We may: implement it from scratch

Listening to changes in the two <input>, compute the addition, and update the result. This solution is performant. However, increasing the number of inputs tends to exponentially scale the complexity of the code. Its usually ends up in bugs and nightmares to maintain.

Or we may: use a front-end framework

Like React with useState or Angular with its ChangeDetector. We'll win in code readability, at the cost of performance lost (when running the change detection algorithm). Moreover, the app will have a bigger size.

More generally, this problematic is applicable to every evolving values, which are nothing more than streams of data.

Some examples:

  • the current date and time updated every seconds
  • a translated text, that changes with the user locale (and maybe some <select>)
  • the state of a submit button (enabled/disabled), if a form is valid/invalid
  • a data stream coming from the backend (ex: live stats)
  • and many more...

Handling the refresh, the combination, the transformation, etc. of such streams, is generally complex. It creates code with low readability, frequent uncovered cases, and inconsistent states.

What if we could have all benefits: performance, code readability, and consistent states ?

The answer

Reactive Programming is the perfect answer to manage streams of data, async flows, and evolving values.

With RP, we build streams of values called Observables. And, we will have an amazing toolbox of functions to combine, create and filter any of those streams. That's where the "functional" magic kicks in. A stream can be used as an input to another one. Even multiple streams can be used as inputs to another stream. We can merge two streams. We can filter a stream to get another one that has only those events we are interested in. We can map data values from one stream to another new one. Etc.

Actually, you're probably already familiar with async data streams: Promises, EventListeners, setInterval, etc. Reactive programming is all about this but on steroids.

You'll be able to create Observables of anything, not just from click and hover events: variables, user inputs, properties, tasks, data structures,...

rp meme

Reactive Programming by example

You've probably heard of RxJS which is the most commonly used RP library in javascript. However, I will promote here @lirx/core, a library strongly outperforming RxJS in performances, having a better documentation and trying to be simpler.

Ok, let's start:

const onInput$ = fromEventTarget(input, 'input');

It creates an Observable for an <input>, listening and emitting Events of type input. It triggers, when the user types something in this input.

An Observable is simply a source emitting values.

An Observable is not started until we subscribe to it:

const unsubscribe = onInput$((event) => {
console.log(event);
});

The provided function is called an Observer, and it receives the values from our Observable.

An Observer is a sink receiving values from an Observable

When the user types in the <input>, its value is simply logged in the console.

Subscribing to an Observable returns an unsubscribe function. When called, it notifies the Observable to stop sending more values, and usually free the Observable's resources.

document.onkeydown = (event) => {
if (event.key === 'Escape') {
unsubscribe();
}
};

Here, when pressing the Escape key, we terminate our Observable, and stop listening to the input's Events.

At this point, you may notice than subscribing/unsubscribing from streams of data is extremely simple with Observables. We don't have to keep a reference to the callback function of .addEventListener for example. It's handled by the Observable itself.

So, we have now an Observable sending input's Events, but what if we want the input's value ? It would be more convenient:

const inputValue$ = map$$(onInput$, () => input.value);

The map$$ function transforms the incoming Events into the input's value. Such a function is called an ObservablePipe.

An ObservablePipe is a function which transform an Observable to another one

So now, when the user types in the <input>, inputValue$ emits this input's value instead of an Event.

And this is where Observables shine: we can combine them, transform their values, create complex data flows, etc. Keeping the complex transformations to @lirx/core and focusing only on the interdependence of our data.


Let's assemble everything to create a custom function building an Observable from an input's value:

function fromInput(input: HTMLInputElement): IObservable<string> {
return merge([
reference(() => input.value),
map$$(fromEventTarget(input, 'input'), () => input.value),
]);
}

Here I have extended a little our first Observable with:

  • reference(() => input.value): this creates an Observable which emits the input's value when subscribed.
  • merge: subscribes to the list of Observables and re-emits all their values.

Here, we will combine merge and reference because the input event is not immediately triggered when we subscribe to our Observable, so we need another way to retrieve the initial input's value.

In short, subscribing to this Observable sends the initial input's value and next values typed by the user.

And if we want to consume our Observable, we simply write:

const inputValue$ = fromInput(input);

inputValue$((value) => {
console.log(value);
});

Alright, let's finish by solving the initial example:

Two <input> in which the user may enter two numbers to perform an addition. The result is displayed right below everytime the user type a new value.

First we have to convert the input's values to numbers:

function fromNumberInput(input: HTMLInputElement): IObservable<number> {
return pipe$$(fromInput(input), [
map$$$(Number),
filter$$$(_ => !Number.isNaN(_)),
]);
}

The pipe$$ function applies many ObservablePipes to an Observable.

We start by applying the map$$$ ObservablePipe to convert the input's values to numbers.

Then, through the ObservablePipe filter$$$, we filter these numbers to keep only the valid ones. We have to do it to handle the cases where the user types nothing or strings which are not numbers.

Finally, to perform an addition from two Observables, we may use add$$:

const result$ = add$$(
fromNumberInput(inputA),
fromNumberInput(inputB),
);

And Voilà ! We have created an Observable performing the addition of two <input>.

Let's recap:

function fromInput(input: HTMLInputElement): IObservable<string> {
return merge([
reference(() => input.value),
map$$(fromEventTarget(input, 'input'), () => input.value),
]);
}

function fromNumberInput(input: HTMLInputElement): IObservable<number> {
return pipe$$(fromInput(input), [
map$$$(Number),
filter$$$(_ => !Number.isNaN(_)),
]);
}

const result$ = add$$(
fromNumberInput(inputA),
fromNumberInput(inputB),
);

result$((result) => {
console.log(result);
});

You can find a demo here

Thinking in Reactive Programming gives us the opportunity to focus on our data flow, and not the complex implementation. Doing the same thing from scratch, requires more code and easily conducts to bugs and poor maintainability.

This example is just the tip of the iceberg: we can apply the same operations on different kinds of streams, for instance, on a stream of API responses, a stream of bytes to compress, a string to translate dynamically, etc.

Why you should consider adopting RP ?

Reactive Programming raises the level of abstraction of our code, so we can focus on the interdependence of events that define the business logic, rather than having to constantly fiddle with a large amount of implementation details. Code in RP will likely be more concise.

Dynamic values are managed in a different way: instead of using some kind of refresh function (to update for example many DOM nodes, if something changes), the variables (here some Observables) are always fresh and updated. So, we won't have to remember to call a refresh() when a change occurs, meaning less bugs and less code in our application.

The benefits are really important in web applications, where we have to deal with a multitude of UI events, async requests, etc... and react to these changes, like updating the DOM.

Moreover, it handles for us any cancellation: from aborting a fetch, to cancelling an eventListener or a setInterval. This point, is frequently ignored by developers creating memory-leaks, or very difficult bugs to fix (like an async tasks that should have been cancelled, but continues to run until it break something - most of the time updating something unwanted).

Cancelling, any pending tasks (ex: from a destroyed html component) is extremely simple with RP.

The drawbacks and advantages of RP

Reactive Programming feels like it is essentially for purists and advanced programmers: it's difficult for beginners and involves complex and abstract logic (everything is a stream, and we must think async).

But in my opinion, the Observables are just like Promises in their time. Do you remember the first time you learned how Promises worked ? This was a new way of thinking our code, and I've seen many new developers struggling to understand how to use them. However, now, I'm pretty sure, you're using Promise frequently without difficulties. They became essential for everyone, because they help so much with async coding. They radically changed our way of developing and thinking our applications.

Well Observables, are just like Promises: at first glance, they seem difficult to handle, but they radically solve every problem related to async algorithms or data streams. They are a very good solution for front-end applications, simplifying fetch calls, dynamic variables in templates, user events, and much more...

And the best part of it: it doesn't impact negatively the performances. On the contrary, when tuned properly, you'll see an uge improvement in your application.

Conclusion

Now, you are ready to begin with Reactive Programming, and discover an amazing world of streams, pipes, and dynamic values.

I hope you enjoyed the beauty of this approach, and maybe I converted you to this awesome way of coding. Even if you're not convinced yet, you should do a quick try, as mastering RP will open you a new world of possibilities.

Create a demo, share it with others, and help to build the web of tomorrow.

rp meme

About @lirx/core

@lirx/core is a Reactive Programming framework with a lot of prebuild functions, clear documentations and many examples to teach you how to become a master of RP. It allows you to develop complex applications and pipelines that scale easily. Moreover, it is the fastest and smallest javascript library for Reactive Programming. So it's a good candidate to start your journey with Observables.

Feel free to test this library, share it and give your feedbacks.