Péter Márton Co-Founder of RisingStack

The goal of this article is not to go into the very details of Functional Reactive Programming. It's more about getting you interested in Node.js streams and the concept of functional reactive programming. Please feel free to share your opinion below.

Intro

Before we get started, I would like to tell you a bit about my relation to Functional Reactive Programming (FRP). I really like the concept and I use it whenever I can without sacrificing the features of the language. I will mostly talk about JavaScript and Node.js.

What I mean: I'm not going to compile to JS from another language to be perfectly functional, I'm not going to force immutability except when it provides reasonable performance as omniscient at rendering. I can also accept that proper tail recursion will arrive only in ES6.

I'm not stating that it would not be good to have immutability for example. I'm just saying that I don't want to have a magic code base with full of hacks, which is both hard to read and understand.

RP, FRP

You may have heard of functional reactive programming. Here’s the gist: FRP uses functional utilities like map, filter, and reduce to create and process data flows which propagate changes through the system: hence, reactive. When input x changes, output y updates automatically in response. - The Two Pillars of JavaScript — Pt 2: Functional Programming

So FRP stands for the Functional Reactive Programming, which is a type of Reactive Programming. I'm not here to make a religious question from this and will use the word FRP in this article. Please don't be too hard with me in the comments ;)

Why FRP is good for me?

Imagine the following scenario:

the user clicks a button it triggers an Ajax call (can be fired only once per every 500ms) and shows the results on the UI.

How would you implement this in the classical way?

Probably you would create a click handler that will trigger the ajax request which will call the UI render .

I mean something like this:

$('#cats-btn').click(function () { if(timeDiff < 500) { return; } getDataFromServer('cats'); // save time }); function getDataFromServer(type) { $.ajax(URL + type).done(function (cats) { renderUI(cats.map(formatCats)); }); } function formatCats(cat) { return { name: 'Hello ' + cat.name } } function renderUI(data) { UI.render(data); }

What is the conceptual problem with this solution?

The code doesn't describe what it does. You have a simple user flow: -1-> click btn -2-> get data -3-> show on ui , but it is hidden and hard coded.

Wouldn't be awesome to have something like the following, a more descriptive code?

_('click', $('#cats-btn')) .throttle(500) // can be fired once in every 500ms .pipe(getDataFromServer) .map(formatCats) .pipe(UI.render);

As you can see, the flow of your business logic is highlighted, you can imagine how useful it can be when you have more complex problems and have to deal with different async flows.

Reactive Programming raises the level of abstraction of your code so you 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. - staltz

Are we talking about promises? Not exactly. Promise is a tool, FRP is a concept.

What about Node streams?

Ok. Until this point this article is yet another FRP article. Let's talk about Node ;)

We have great FRP libraries out there like RxJS and Bacon.js (by the way Bacon has the most hipster name and logo in the universe) which provide lots of great functionality to help being reactive. BUT...

...everytime when I read/hear about FRP and event streams, the first thing comes to my mind is that Node has this beautiful stream interface. But most of the popular FRP libraries just do not leverage it. They implemented their own stream-like API.

They are providing some compatibility with Node streams like: Rx.Node.fromStream() , Bacon.fromBinder() but they are not fully compatible with it. This makes me sad.

Node.js is already on the client side with browserify and webpack, npm is full of great stream libaries and we cannot use them out of the box.

I was wondering why they don't use it but I didn't find anything useful. Please comment if you have something in your mind about this.

But can't we, really? Come on, it's Node land. Of course someone has already done it, it's called Highland.js:

...using nothing more than standard JavaScript and Node-like Streams

Highland is created and maintained by @caolan, you know the guy who created async too.

Dominic Tarr also implemented the event-stream to make our life easier with streams, but it has less features compared to Highland.js, so let's continue with that.

Playing with Highland and node streams

Prerequisites: we are on the client side using a browser and our code is bundled by webpack.

You can find the full runnable code on GitHub.

// from node var util = require('util'); var stream = require('stream'); // from npm var _ = require('highland'); var websocket = require('websocket-stream'); var catWS = websocket('ws://localhost:3000');

Then we create a native Node.js writable stream to write to the console, but it could have been a jQuery append or anything else.

var toConsole = new stream.Writable({ objectMode: true }); toConsole._write = function (data, encoding, done) { console.log(data); done(); };

Then we create our filter function for .filter()

function underThree (cat) { return cat.age < 3; }

The main application: easy to understand what it does, right?

_(catWS) .map(JSON.parse) .sequence() .filter(underThree) .map(util.format) .pipe(toConsole);

I think this is a good example how easily you can describe with code what your application does.

This is a simple example with a one way flow, you can handle much more complex async problems with the merge, ratelimit, paralell methods.

For more functionality, visit the Highland.js documentation.

Streams for the web

Proper streams are coming to the browser and Domenic Denicola already gave a talk on it: Streams for the Web. I can just hope that it will arrive soon and will be fully compatible with Node.js's streams. It would be awesome.

Update:

If we want to be accurate, Highland.js, Rx and Bacon.js aren't FRP: