Thoughts on RxJS

Brian Di Palma
4 min readJan 1, 2016

RxJS was mentioned as part of the discussions around a technology refresh at Caplin. After looking into RxJS I was underwhelmed and didn’t see it as a clear win for us, while some of the reasoning is company specific I’d argue that most of it is generic and widely applicable.

RxJS’s purpose is to deal with asynchronous data streams/work flows. For this purpose RxJS provides three features, to quote the RxJS repository:

RxJS = Observables + Operators + Schedulers

There are two types of asynchronous data streams, singular and plural. Singular are where the receiver is sent one piece of asynchronous data to satisfy the task, plural is where the receiver can be sent one to possibly
infinite pieces of asynchronous data over the lifetime of the task.

At Caplin our two main sources of asynchronous data streams are:

  1. SLJS (similar to Socket.IO/WebSocket streaming connection).
  2. User GUI interactions.

To justify the learning ramp and extra abstraction layer of using RxJS we needed to feel confident that RxJS is superior enough over our current approaches in those areas to payoff the expense.

Network

The first source of asynchronous data streams, SLJS (a library we created to stream real time price updates), is currently interfaced with by creating subscription class instances. As these instances can be sent an infinite stream of data they are plural asynchronous data streams. The API is more focused than Socket.IO/WebSocket with some domain specific value.

My feeling is that SLJS subscriptions are fairly straightforward to deal with. They are simple classes, normally requiring the implementation of a few methods. Once created the class instance is passed to SLJS specifying a subject (similar to a room in Socket.IO) which the subscription is interested in receiving updates for.

class MyFXSubscription {
...
onRecordUpdate(subscription, event) {
console.log(event); // New price tick.
}
...
}
const subscription = new MyFXSubscription();streamLink.subscribe("/FX/EURUSD", subscription);

This is similar in power to an Observable so it seems wasteful to either wrap this library in RxJS Observables or to rewrite it to provide RxJS Observables.

The judgment would be similar with WebSocket, Socket.IO or most other real time subscription based abstractions so this isn’t specific to our streaming solution.

Real time streaming isn’t the common case when it comes to asynchronous network communications in web applications though. More common would be singular asynchronous data streams. Make a single request to an endpoint and carry on execution once it resolves/fails. RxJS seems overkill for those situations, a simple arrow function or Promise like the fetch specification uses is enough.

GUI

Singular asynchronous data streams are also the common case in the second source of asynchronous data streams, user GUI interactions. These are situations where an operation is waiting on one user input and once that input is provided the work flow is complete. For example a dialog/modal which can set a value in a model or if closed/dismissed will carry out another or no action. An arrow function or Promise is sufficient in these cases too.

GUI interactions with plural asynchronous data streams are where there is a
stronger case for using RxJS Observables. The canonical example is the autocomplete input box where the user can type any number of characters and the GUI has to update a drop-down list whose data is populated asynchronously. I think these use situations are rare and can be handled without RxJS.

Drawbacks

RxJS doesn’t just seem unnecessary but it might in fact be detrimental to a codebase. The danger in using RxJS is that you end up wrapping all your business data in RxJS Observables. We had a similar problem at Caplin with Knockout. Once we started using Knockout almost all our front end business data ended up being wrapped in Knockout Observables even when there was no reason to do so.

I think it’s hard to draw a line between what should and should not be wrapped in RxJS so I think it’s quite possible the same mistake would be repeated. In essence you will end up trapping your state tree in these data structures, possibly making it more complex to support client side rehydration, hot module reloading, time travel debugging etc.

I feel code that uses RxJS is more prone to such problems due to the fact that RxJS does too much. It not only provides an Observable type but a large set of operators that allow you to manipulate the observables and their data. I can imagine it’s hard to resist using the provided operators even when there is no need. There are many RxJS code examples where they aren’t even dealing with asynchronous data streams. There are also problems around debugging RxJS.

Simple functions and primitive data types provide maximum flexibility, reusability potential and the lowest ramp up learning time.

ECMAScript

One reason that you might be tempted to use RxJS is that one part of it is inspiring an ECMAScript proposal. To me that seems a weak reason. Currently Observable is at stage 1 in the ECMAScript process, it’s at an early stage, and it’s not likely that it will be in ES2016. The Observable proposal is also just one way to handle asynchronous data streams there are others e.g. async/await, CSP/channels, event emitters, streams or maybe even an actor system.

Arrow function callbacks, Promises and async/await are going nowhere and can work in the vast majority of use cases. For that reason I felt the most reasonable approach was to use them, skip RxJS and wait for Observable to mature before pulling the trigger on it.

My suspicion is that RxJS is a bit like jQuery, you probably don’t need it as much as you think you do, it offers less value than you believe and once you start using it it can spread all over your codebase making it hard to remove.

--

--