We can hear more and more about RxJava, Reactive extensions or Reactive programming in general nowadays. The hype is increasing, many tech conferences covers the topic of those concepts . We can observe moving from imperative to functional-reactive approach in writing applications. Languages like Scala are gaining popularity. Also release of Java 8 was great step forward in adding more functional approach, usable for more efficient modeling of behaviors in object oriented design.
The fact is that paradigms are changing, programmers are leaving old ways of creating software in order to satisfy changing requirements and business needs in creating modern applications. Internet is growing rapidly nowadays. A single website like Facebook can handle such as load this days as the entire Internet did a decade ago.
So, is the reactive programming another trend which comes and go? A cool, new thing to learn? Or maybe it has significant importance in creating modern web and mobile applications? Before answering that question let’s find out what is behind of that concept.
What is Reactive programming and RxJava?
Reactive programming is a programming paradigm like object oriented, functional or imperative programming. Hoverer, it has much in common with functional programming.
The Reactive Manifesto, an online document that sets new standards and requirements for applications developments states that reactive systems should be:
- Responsive – system provides rich, real-time interaction with its users
- Elastic – system can adapt to an intensive workload and stay responsive
- Resilient – system is able to deal with failure and stays responsive when some components crash
- Message Driven – in order to achieve first three requirements, system needs to rely on asynch message-passing between components to ensure isolation and loose coupling
RxJava, on the other hand, is a most popular framework which provides an API to create reactive systems in Java. It is implementation of Reactive programming principle, some kind of toolbox.
But what is exact definition of reactive programming?
The definition states that reactive programming is about composing non-blocking, event driven programs by using asynchronous, observable stream of data. Let’s split it into parts.
What is stream? Stream is a sequence of data over time. The simplest stream can be numbers that increases by 1 every second like this:
[0,1,2,3,4,5]
Another example could be sequence of mouse click positions on screen in a browser game:
[(1,43), (23,899), (15,34)]
There could be also streams of database queries, HTTP requests, notifications, changes of variable, object states or even failures, anything that may change or happen. Those events are not occurring at the same time. There is a timeline involved, they may happen at some time in future. This is a quintessence of asynchrony and the core of reactive programming.
So, we have a stream. Programming in reactive way relies on observing the stream and handling appearing events asynchronously in non blocking manner. It also let you to transform one stream to another one or create multiple streams and combine them together. Some will say that you may use callbacks to do the asynchronous invocation. But if you do so sometimes you may end up with callback hell. Having one callback inside another leads to very complex codes which are hard to maintain.
Before I will show you some examples in RxJava, I will describe two important features in reactive programming:
Back pressure
Back pressure is one of those concept. Can be describe as the strategy of handling too fast data production. In practice, it is used in situations where producer emits more information than consumer can process. For example, making a query to a database that returns huge number of rows. Without back pressure, database will return all rows and if consumer won’t be able to retrieve them, it will block. Program will be suspended, threads will be waiting for unlocking.
With back pressure, the consumer is more elastic: he will ask first for some part of database query result. If he will be able to handle more, he will ask for another set of rows. Also there are some operators that allow you to deal with situations that there is no choice and you might have to buffer or drop that excess.
Non blocking
It is the most important aspect of Reactive programing and it is main reason of improved performance in applications written in reactive way. Multi-threading in reactive programming is more similar to a Node.js non-blocking event loop model than concurrency model know from Java with multiple threads. With non-blocking model you get rid of overhead from maintaining many threads and context switching.
Let’s jump in more technical part and see how to use RxJava in practice.
RxJava in practice
RxJava defines stream as Observable<T> type. We create a simple Observable this way, using static methods from RxJava API:
Observable.just(1); Observable.just("one", "two", "three"); Observable.fromIterable(usersCollection); Observable.fromFuture(future);
Streams are lazy. Unless we start to observe them, nothing will happen. We start observing and receiving data emitted from Observable by using subscribe() method:
Observable.just(1, 2, 3) .map(i -> i*10) .filter(i -> i > 15) .subscribe(System.out::println); //Output: 20, 30 //example with two combined streams Observable<String> strings = Observable.just("A", "B", "C"); Observable<Integer> ints = Observable.range(1, 3); Observable<String> result = Observable.zip(strings, ints, (s, i) -> s + i); result.subscribe(System.out::println); //Output: A1, B2, C3
First example shows operation on a stream of data known from Stream API in Java 8. After subscribing, we get the result in console output. In second example I used two data sources and combined them into one stream using zip() method.
Ok, looks nice, but where those asynchronous magic happens? Let’s simulate some business logic with time added to the equation.
Some more real world examples
Assume that you have an application that updates temperature for your city every minute. See how to do it in RxJava:
Observable.interval(60, TimeUnit.SECONDS) .flatMap(n -> weatherRepo.getWeather("Cracow")) .map(weather -> weather.getTemperature()) .subscribe(System.out::println);
In example below, Observable created with interval() method is fetching the temperature from repository every minute. FlatMap is used because we need to flatten result of getWeather() on Observable<Weather> type. That example also shows the beauty of declarative programming, the code is easy to modify and we didn’t use any for or if statement.
But it is still synchronous. We are getting updated temperature every 60 seconds, not in some undefined time in future. Look at the last example. Let’s say we have two DAO classes with method that returns an entity from the server using dummy fetchFromRepo() method that simulates retrieving request from DB.
public class UserDAO { public Observable<User> findById(int id) { //simulate HTTP request Sleeper.sleep(Duration.ofMillis(1000)); return Observable.fromCallable(() -> fetchFromRepo(id)); } } public class CourseDAO { public Observable<Course> findByName(String name) { //simulate HTTP request Sleeper.sleep(Duration.ofMillis(1000)); return Observable.fromCallable(() -> fetchFromRepo(name)); } }
fromCallable() method accepts lambda and returns an entity packed in Observable.
private UserDAO userDAO = new UserDAO(); private CourseDAO courseDAO = new CourseDAO(); Observable<User> user = userDAO .findById(44); .subscribeOn(Schedulers.io()); Observable<Course> course = userDAO .findByName("Sample course"); .subscribeOn(Schedulers.io()); Observable<String> result = user.zipWith(course, (User u, Course c) -> u.toString() + " : " + c.toString()); result.subscribe(System.out::println); //Output User42 : Sample course
In this example we used zipWith() method to combine events from two input streams into one event on the output stream.
We also divided loading Course and User into two different threads. What is important: In this example we passed findById() and findByName() method invocation to separate thread pool (it is behind Shedulers.io()) using subscribeOn() method which enhanced speed of whole operation. It is very simple example but assume that it is a part of larger application with many clients requests per second. In such case, every client request’s will be delegated to separate thread. That is significant speed improvement of such web service.
What are benefits of using RxJava and programming reactively and why you should consider to use it:
- With Reactive programming you could do more with less, what means that you are able to handle higher load with fewer threads with non-blocking event loop model. It is also less hassle that regular threading, so it could save time while developing
- You can write a code in declarative way – reactive programming uses functional approach. It leads to less error-prone programs with reduced boiler plate code
- Many programming languages have reactive extensions, not only Java. To name a few: RxJS, Rx.NET, RxScala, RxClosure, RxCpp
- Avoiding “Callback hell”
There are of course some disadvantages:
- High learning curve: the reason is not only new API to learn but also makes programmers to shift their thinking into functional way of programming
- Some memory leaks could happen, caused by handling subscriptions incorrectly
- It is easy to turn some synchronous function into asynchronous especially when implementing reactive extension into some legacy code
Conclusion
It would be exaggeration to say that you should make everything reactive these days. That approach has lot of advantages: it offers a better user experience, applications are faster and capable of higher load achieved with less costs. But for sure it isn’t the one and only right way of making software nowadays, because there isn’t any best way to do it in general. It also doesn’t mean that older paradigms like Object Oriented Programming or imperative approach are passe and all business and developers should dive into reactive or functional way. Although reactive programming nicely fit intoย the requirements of modern business,the final decision depends on individual client needs, project size, technologies used in and also developers capability to learn new approach and think in reactive way.
Our mission is to support startups in achieving success. Feel free to reach out with any inquiries, and visit our blog for additional tips. Tune in to our podcast to glean insights from successful startup CEOs navigating their ventures.