An Intro to RxJS and Observables: a General Version of Promises

Asynchronicity is arguably the hardest part of Javascript development. I’m assuming you’re already familiar with promises. If not, you can read about it and come back when you’re done. Understanding promises is crucial for modern web development.

Promises and RxJS

Promises solve Ajax callbacks very well. Remember the days when you had to handle multiple parallel Ajax requests with callbacks. How did you handle it if one fails? It probably wasn’t pretty. Nowadays, you probably use promises whenever you need to pass data between services. Browser to server, server to server, server to database, etc. These are all asynchronous requests well-suited for promises. However, there are other asynchronous actions that promises aren’t good for. Specifically, asynchronous actions that need to call the callback multiple times. In other words, events. You can think of everything that promises can handle as events that fire once. E.g. when an Ajax call completes, it calls the Ajax success event, which triggers the callback. RxJS can handle asynchronous actions that fire multiple times, such as events. RxJS allows you to operate on events as you were operating on an array. If an event fired 3 times, it’s like an array with 3 elements.

RxJS and Design Patterns

If you’re unfamiliar with design patterns, you should skip the rest of this section. I will explain RxJS without using design patterns in the next section. RxJS was created under the idea that observables and iterables are essentially the same. This is very unintuitive because observables emit events and iterables are sequences of values, how could they be the same? Imagine 2 objects: an observable and a callback function. When you want call the callback function, you fire an event on the observable. The callback function can return a value. You can use the value however you want in the observable. Now imagine 2 other objects: a program and an iterable. When you want a value from the iterable, you call next on the iterable. The iterable returns a value which you can use in your program. It may seem like a big difference here is the time factor, but that’s actually a rather small difference. If your observable calls callbacks synchronously and you emit multiple events in one event loop frame, then all those callbacks run in the same frame. If you add a setTimeout before fetching the next iterable element, then the elements are fetched at different times.

Typical observable:


observable.addListener('click', callback);

setTimeout(() => {
    observable.emit('click');
}, 1000);

setTimeout(() => {
    observable.emit('click');
}, 2000);

setTimeout(() => {
    observable.emit('click');
}, 3000);

Typical iterable:


let iterable = [1, 2, 3];
for (let val of iterable) {
    doSomething(val);
}

Observable that looks like an iterable:


let n = 1;
observable.addListener('getNext', () => {
    ++n;
    return n;
});

for (let i = 0; i < 100; ++i) {
    doSomething(observable.emit('getNext'));
}

Iterable that looks like an observable:


let eventData = [1, 2, 3];
function waitForEvent() {
    setTimeout(() => {
        callback(eventData.shift());
        waitForEvent();
    }, Math.random() * 1000);
}
The main difference between iterables and observables is which object has control. For observables, the observables has control. It can emit events whenever it wants and the callback has to run. For iterables, the program can pull data from an iterable whenever it wants. RxJS uses this parallel between observables and iterables to create observables that look like iterables. RxJS allows you do manipulate observables as if they were just iterables.

What can RxJS do?

RxJS makes it much easier to deal with events by allowing you to manipulate them as if they were arrays. An autocompleted input is a classic example of where RxJS excels. It would look something like this:

Rx.Observable
    .fromEvent(document.getElementById('autocomplete-input'), 'input') // When input changes.
    .debounce(500) // Wait until user stops typing for 500ms.
    .map(event => event.target.value) // Get input's value.
    .filter(value => value.trim().length) // Ignore if input is empty.
    .distinctUntilChanged() // Ignore if input's value is same as last value.
    .flatMap(value => fetch(
        API_AUTOCOMPLETE + '?input=' + encodeURIComponent(value)
    )) // Get autocomplete data using HTML5 fetch.
    .last() // If previous API call didn't finish yet, ignore it.
    .retry(1) // Retry once if it failed.
    .subscribe(
        autocompletes => handleSuccess(autocompletes),
        err => handleError(err)
    ); // Do something with the response.
How would you do this without RxJS and observables? You would have to implement debouncing, check if the value changed, ignore pending API calls, and retry if it fails. Not only would the code be much longer, you would also have variables such as currentFetchRequest and numRetries to keep track of the state. This makes it prone to bugs such as race conditions. RxJS makes everything easier to write and read.

How do I use RxJS?

You can install RxJS using npm install rxjs. If you’re not using a framework, you can use RxJS as you would use jQuery. You can gradually replace parts of your codebase with RxJS. If you’re using React, I recommend using redux-observable. RxJS is incredibly flexible, just like jQuery or Underscore. This means that there are many ways to use RxJS with React. You can either implicitly enforce a certain application structure or you can use something like redux-observable to enforce a structure for you. Redux is built under the same paradigm as RxJS: reactive programming. In fact, Redux is essentially one line of RxJS:

actions$.scan(reducers).subscribe(render); 
I personally think RxJS makes a lot more sense than Redux. redux-observable gets you the best of both worlds. If you’re using other frameworks, there are utility libraries for most popular frameworks, such as the official bindings for Angular and vue-rx for Vue. However, since RxJS is just a utility library, you can use it with any framework without any additional libraries. RxJS has been called “Lodash for async”, so you can use it in your applications as if you were using Lodash or Underscore.

What’s the future of RxJS?

Observables are proposed to become an official part of ECMAScript (https://github.com/tc39/proposal-observable). RxJS will act as an observable polyfill and a library of helper functions for the built-in observables. RxJS is well-regarded by Javascript developers, but its adoption is slow because it’s most valuable for large complex website. Newer websites don’t need RxJS because they’re often not complex enough. The existing large complex websites would need to rewrite a large portion of their codebases to use RxJS. RxJS is used for new codebases that are anticipated to become large and complex. Even if you don’t need to use RxJS in your projects yet, it’s an incredibly useful tool to learn for when you need it.

2 thoughts on “An Intro to RxJS and Observables: a General Version of Promises”

Leave a Reply

Your email address will not be published. Required fields are marked *