Greg Babiars's Blog

As Much RxJS as You Need

February 21, 2016

Note: Examples in this post use RxJS 5

Recently I have been using RxJS in work apps, starting with using it to coordinate async tests and recently replacing complex data flows. I have also started playing with it for non-work stuff including some with Cycle.js. The ability to use observables a little or fully buy in has led me to realize one of the main reasons I like RxJS: you can use as much as you need!

Being able to use just the parts you need is very nice since it allows integration with other libraries and frameworks. Since the observable is such a powerful primitive, it also allows the building of frameworks like Cycle.js where everything is an observable.

Just a little

RxJS can be very approachable as someone new because you can introduce it slowly to your project. A simple example of this would be if we wanted to use observables instead of promises for our data layer. Here is a simple example:

import Rx from 'rxjs/Rx';
import 'rxjs/add/observable/dom/ajax';

export default function get(url) {
  return Rx.Observable.ajax.get(url).toPromise();
}

The toPromise operator converts our observable to a promise so we can keep our existing promise based api. Let’s say we wanted to add some retry in case we get random network errors:

import Rx from 'rxjs/Rx';
import 'rxjs/add/observable/dom/ajax';

export default function get(url) {
  return Rx.Observable.ajax.get(url)
    .retry(2)
    .toPromise();
}

This code simply says if we get an error, retry 2 times and fail error out after that. We could also use the retryWhen operator to have more complex retry logic:

import Rx from 'rxjs/Rx';
import 'rxjs/add/observable/dom/ajax';

export default function get(url) {
  return Rx.Observable.ajax.get(url)
    .retryWhen(errors$ => {
      // more complex retry logic
    })
    .toPromise();
}

With this we can do things like refresh an auth token when we receive a 401 and then retry the previous request after. What makes RxJS so appealing is that the data flow is followable even with complex things like retry logic. The best part is that we don’t need to change the exposed api.

If we ever did want to change our consumer code to use observables, we simply remove toPromise() from the get function and change our consumer code as such:

// before
get(url).then(data => {
  // success
}, err => {
  // error
});

// after
get(url).subscribe(data => {
  // success
}, err => {
  // error
});

Interoperability

When using a new library, it is very important how it will play with other frameworks and libraries you use. RxJS gives us many helpers out of the box that allow us to interop with other ways of doing asynchorous programming in JavaScript, mainly promises and callbacks.

As we saw in the previous section the toPromise operator conveniently converts our observable to a promise. The operator fromPromise is also provided which takes in a promise and creates an observable. This is very useful if you want to consume something like jQuery’s ajax promise or some other library that returns promises.

RxJS provides two helpers for dealing with callbacks: bindCallback and bindNodeCallback. These allow you to wrap functions and return an observable representing the result of the function.

Taking it further

While you may want to just introduce a little RxJS to your codebase, you may end up wanting to use it more and more. Luckily there are many projects that are starting to take RxJS as a dependency. Angular 2 uses RxJS for many internal pieces. If you use React, rx-react and thisless React allow you to observables for state and actions. Lastly, Cycle.js is a small framework built using RxJS observables to define the entire flow of your JS application.

Taking it for a spin

The RxJS team has done an amazing job of providing a primitive and utilities that can interop with other libraries and frameworks in the JS space. Because of this, adopting it is a very low commitment. If you haven’t already tried it out, I highly recommend giving it a chance.


Greg BabiarsWritten by Greg Babiars who builds things for the web. You can follow me on Twitter.