Functional Reactive Programming and JavaScript
Functional reactive programming (FRP) is an emerging programming paradigm that has recently gained popularity in the web world. That popularity isn’t just tied to fact that the video streaming giant Netflix blogged about how it applies FRP to optimize its API, but because it actually provides an elegant way to reduce the complexity of dealing with time-varying events and asynchronous operations.
An Example
Let’s look at an example to see how FRP can solve a problem that you have likely had to deal with in JavaScript before – when asynchronous requests to the server don’t return in the order they were requested. For the sake of demonstration, let’s say we have a requirement for a cascading drop-down list that consists of 2 related drop-downs. When you select a value from the first one, an AJAX request will be issued to get the list of values to fill up the second drop-down. You can implement in jQuery (or any other JavaScript framework) something like this:
<script>// <![CDATA[ $('#dropdown1')change(function () { var selected = $(this).val(); var promise = $.ajax('/path/to/your/server', { data: {value: selected} }); promise.then(function(data){ var html = $.map(data, function(item){ return '<option value="' + item + '">' + item + '</option>' }).join(''); $('#dropdown2').html(html); }); }); // ]]></script>
The problem arises when user is rapidly selecting different values from dropdown1 and the responses are out of order in which they were requested. You could end up having invalid values in dropdown2. One way to solve this problem is to disable dropdown1 until the response comes back to prevent the user from selecting different values. Unfortunately, this makes the application less responsive as the user can’t interact with dropdown1 input. That is when FRP gives you an idea – what if you can make these responses observable, like an event stream, where you want it to react to only the latest response in the stream. There are two popular FRP JavaScript libraries: Bacon.js and Rx.js. I will use Rx.js in this example. The idea is to push these response promises as soon as you get them into an observable event stream. The library will unwrap these promises once they get resolved into yet another stream where our application will be observing. It will react only to the last event. Rx.js doesn’t have a event bus out-of-the-box where you can push values into and observe. (Unlike Bacon.js, where you do have a Bus class that can do this). However, you can create your own event bus with ease, or find one that is already implemented like this one. With the help of the library, our code looks incredibly simple:
<script>// <![CDATA[ var bus = new MessageQueue(); $('#dropdown1').change(function () { var selected = $(this).val(); var promise = $.ajax('/path/to/your/server', { data: {value: selected} }); bus.push(promise); }); bus.flatMapLatest(function(promise){ return Rx.Observable.fromPromise(promise) }).subscribe(function(data){ var html = $.map(data, function(item){ return '<option value="' + item + '">' + item + '</option>' }).join(''); $('#dropdown2').html(html); }) // ]]></script>
First, we create an observable stream called bus to which we can push value. Then we subscribe to the stream so we can react when the value (the response) changes. The flatMapLatest filter method is the one that doing all the magic here. What it does is unwrap the promise when it get resolved into another observable stream. But, while it is waiting for the promise to get resolved, if there is another promise that comes after, it will switch to that stream instead. There is no need to disable the drop-down input. The user can try to select another value if he/she mistakenly picked one, not having to wait for the previous response to come back. The second drop-down is guaranteed to be in sync.
Final Thoughts
For a small application like this, FRP doesn’t really offer any more advantages than a functional programming or procedure programming. But as the system scales up in these perspectives such as number of interactive components, concurrencies, number of asynchronous operations (and callbacks), the FRP paradigm begin to offer abstractions that help developers not worry about trigger reevaluation of time-varying expressions. I hope you find this article useful and intriguing enough to explore more about FRP. Enjoy.
Reference: | Functional Reactive Programming and JavaScript from our JCG partner Phuong Nguyen at the Keyhole Software blog. |