~/

Back

Reactive Series (pt. 1) - Why You Should Consider Reactive Programming


In the first part of my series on Reactive Programming, I want to answer why you should consider Reactive Programming. My answer to this question is heavily based on my experience working on building complex user experiences that often deal with asynchronicity in the browser. I won’t cover much on how Reactive Programming, would help your platforms scale by providing out of the box solutions to handle backpressure. Still, I believe that the principles throughout this series can be extended to other ecosystems/platforms/programming languages.

»From callbacks to async/await

At first, callbacks were the primary way for us to handle asynchronicity in JavaScript. Callbacks allow us to write code that is not yet ready to be executed because its parameters depend on the execution of some task(s) that will be complete in the future. Fastly we began to gain acquaintance with the callback hell.

doSomething(param1, param2, function(err, paramx) {
doMore(paramx, function(err, result) {
insertRow(result function(err) {
yetAnotherOperation(someparameter, function(s) {
somethingElse(function(x) {
// ...
});
});
});
});
});

Somewhere down the road, jQuery 1.5 introduced Deferred Objects to manipulate callback queues and provide an alternative to manage asynchronicity. By this time, the concept of Futures and Promises was not new to computer science.

It was not until ECMAScript 2015 - The 6th edition, initially known as ECMAScript 6 (ES6) - that we got Promises as a standard built-in object in the language. Before landing in ES6, Promises had already disrupted the JavaScript ecosystem. Libraries such as bluebird, which has been around since 2013, have made it possible for JavaScript programmers to handle asynchronous code with Promises. But why Promises were such a game-changer? Mainly because they provide a higher abstraction on top of the callback pattern. With Promises, you get a reference to an object that holds the resolution/failure of some future value. That reference is chainable. You can easily return a Promise from a function and proceed with your program flow on the callee instead of propagating the chain of events down through the callback hell. Great!

function something() {
return doSomething(param1, param2)
}
function main() {
something().then((err, paramx) => {
// do stuff
})
}

Again a new anti-pattern is born. We tend to attach to names emotionally, so the “hell” is back, but this time, instead of callback hell, we have the Promise hell.

function something() {
return doSomething(param1, param2)
}
function main() {
something().then((err, paramx) => {
doMore(paramx).then((err, result) => {
insertRow(result).then((err) => {
// ...
})
})
})
}

The thing with Promises is that they’re also tempting to nest, resulting in extremely verbose code.

In the 7th and 8th editions of ECMAScript, we are given the async function, a new mechanism to battle the complex challenge that is asynchronicity. This time we have an even higher order of abstraction, where inside an async function, you can handle asynchronicity and making it look exactly like synchronous code. It seems a very promising breakthrough, and the community goes all in. Although async functions are just syntactic sugar on top of Promises, they bring us enormous advantages:

After broad usage of async/await, the community started again, raising some cons on the construct:

I want to reinforce that we came to a long road since callbacks, and the enhancements are noticeable, but after some time, mentions to the “async/await hell” started to surface across the web.

;(async () => {
const pizzaData = await getPizzaData() // async call
const drinkData = await getDrinkData() // async call
const chosenPizza = choosePizza() // sync call
const chosenDrink = chooseDrink() // sync call
await addPizzaToCart(chosenPizza) // async call
await addDrinkToCart(chosenDrink) // async call
orderItems() // async call
})()
example from:{" "} "How to escape async/await hell"

Generally speaking, something I learned over time, is that if a solution has many corner cases, requiring you to look into the documentation (e.g., error handling in async/await VS Promises), it means that:

But what if everything is asynchronous? What if synchronous and asynchronous code looks the same? I believe this is the best way to shape our mindsets into producing highly readable and performant code. How would we achieve that? Observables, Reactive Programming.

But Reactive Programming is not only about tackling the complicated job of handling asynchronicity. There are other amazing advantages that this paradigm facilitates:

There’s one thing I need to mention, though, which is what most of the above sources where I’ve been reading about Reactive Programming have in common. Although we’ve seen Reactive Programming taking the higher ground in several aspects compared to traditional paradigms, its complexity seems to be one of the main barriers to its adoption. Reactive implementations are said to have a higher maintenance cost compared to a conventional programming style. I can’t argue this, the learning curve is steep, but once you pass that the rewards are noteworthy and things that in other times you would think of impossible or had to implement become so much easier.

If you feel like taking a shot at Reactive Programming, I’ll gladly guide you through it!


Last modified on 12, Oct, 2024