As a software developer, I must deal with asynchronous programming on a daily basis. In order to provide the best user experience possible, all tasks like performing a server request, getting data from my database, waiting for some background process to finish or downloading an image should be executed asynchronously.
Even with some years of experience, I sometimes forget the syntax for a particular asynchronous call. How should I implement the callback for a specific task and how should I handle the error if anything goes wrong? There are hundred of ways of dealing with responses, and as a developer it’s my job to know which one fits best in every situation. When I first read about ReactiveX I thought: “Great, another asynchronous API to memorize…”. Well, I couldn’t be more wrong.
The ReactiveX (or Rx) API standardizes all the asynchronous processes with a very simple syntax:
onCompletion(). No more having to know the exact code syntax to handle that callback that I only write twice a year. Not to mention that the code looks way more beautiful because of all the boilerplate I don’t have to write anymore.
It is based on three main ideas: the Observer pattern, the Iterator pattern and functional programming. The Observer pattern is an awesome way to deal with asynchronous tasks: every time a new event is triggered on the observable it notifies all its receivers. The Iterator pattern allows a better decoupling between the observable object’s type and the algorithms, thus allowing it to be used in a wider range of scenarios. Functional programming not only makes the code more concise, but it also helps a lot on these concurrent scenarios. Those ideas combined make ReactiveX a great way to deal with asynchronous programming.
It’s a pretty straightforward API, once you understand how observables, observers, operators and schedulers work. Let’s take a deep breath and dive right into it.
Observables and observers
This part is quite simple: observables will emit events, as I mentioned before, and some other objects (called observers) will subscribe to those observables and will handle those emitted events.
Here is where a great deal of the Rx magic happens.
Let’s say that you want to listen to changes on a search field: get the user input, create a request for your backend and present the search results to the user. You know that you shouldn’t react to every single new character the user types in, or a fast typing user would create tons of requests in a very short period of time, which would be harder to handle and also put a lot of pressure on the backend. What could you do in this case? Probably create a timed task that checks every couple of seconds if the search field has any changes, and if it does, send the search request. This implies in more code, and more room for bugs (in my experience, timers are a great source of issues). What if I told you ReactiveX can handle all of that with a single line of code? Seems like magic, right?
I’m talking about operators now. Most of these guys operate on an observable and return another observable, allowing you to chain multiple of them to achieve the desired behavior. In the search field example I mentioned before a single debounce operator would achieve the behavior we wanted.
The debounce operator filter all the events that are triggered in a time window. After the window ends, it maps the last triggered event that happened to the new observable.
There are operators that can create, transform, filter, combine observables and much more. You can have a pretty good idea on how to use them here.
Schedulers make me want to use Rx for every single async task I have.
Some special operators take schedulers as a parameter to allow multithreading in the cascade of operators. This allows Rx to start handling an event emitted by an observable on a thread, process part of it on another thread and present the result on a third thread.
Each color represents a thread where the operators will perform their transformations. The process starts on the blue thread because there is a
subscribeOn(blue) somewhere in the loop (this operator can only appear once). Each time an
observeOn() appears, the processing thread for the processes below it changes, this way most of the mapping happens on the orange thread and the last chained observable will be observed on the pink thread.
This may sound like a lot of threads, but the first time you start a network request on a I/O thread, process the response on a background thread and update the views on the main thread with just three extra operators, you’ll see how valuable schedulers are.
Where can I use ReactiveX?
You may be wondering: will ReactiveX fit on my project? The answer is probably yes. It has been implemented on many different languages and platforms – you can check them here. Don’t panic if you don’t find your language or platform there, you can probably find a project on GitHub that already implemented a library for you.