What is Reactive Programming?

Reactive programming is a paradigm that moves away from traditional, imperative programming techniques. Instead of defining step-by-step instructions, it emphasizes describing a system as a set of components and specifying how they interact. These components collectively form the foundation of an application. This approach aligns well with object-oriented programming principles, enhancing its effectiveness in application development.


Today, most popular programming languages—such as JavaScript, Java, C#, and C++—support reactive programming, either natively or through specialized frameworks. In Java, two major solutions are Project Reactor and RxJava. These frameworks enable developers to structure code into composable, independent blocks that can be chained together. These blocks do not necessarily execute on the same thread; instead, each block runs on whichever thread is available, promoting efficient use of system resources.


The main difference between traditional and reactive solutions is that reactive programming emphasizes asynchronous, non-blocking code execution. Instead of a thread being stalled while waiting for an operation to complete, the thread is freed up to process other tasks, and the underlying task resumes when data becomes available.
Reactive programming works well with event-driven architectures. It excels in scenarios where we want to execute a block of code and respond only when output is received. Moreover, the output can be processed incrementally as it arrives, allowing for efficient stream processing.

Java frameworks also provide robust error handling in reactive chains, enabling different behaviors to be triggered when errors occur. They promote a fluent, functional style of structuring code, which is now the preferred way of developing applications.


These frameworks are fully compatible with popular technologies for building web applications. For example, the Spring Framework allows us to build fully reactive back-end web applications. Other technologies, such as Apache Kafka, can also be viewed as reactive systems, enabling us to process changes incrementally as data arrives.


Below is an example that returns usernames from a service and prints the results, first in a traditional way and then using reactive programming:

// Imperative style
List<String> userNames = userService.getUserNames(); // blocking call
for (String name : userNames) {
    System.out.println(name);
}

// Reactive style
userService.getUserNamesReactive() // returns Flux<String>
    .doOnNext(System.out::println)
    .subscribe();

We can also define an exception handler using doOnError():

userService.getUserNamesReactive() // returns Flux<String>
    .doOnNext(System.out::println)
    .doOnError(error -> System.err.println("Error: " + error.getMessage()))
    .subscribe();

Here is an RxJava example that defines both onNext and onError handlers in a single subscribe call:

userService.getUserNamesObservable()
    .subscribe(
        System.out::println,
        error -> System.err.println("Error: " + error.getMessage())
    );

In summary, reactive programming offers a powerful alternative to traditional paradigms. Its main benefits include ease of use, interoperability with modern technologies, and the ability to structure code into independent, composable components. Supported by a wide range of frameworks and languages—including RxJava and Project Reactor—reactive programming also provides robust error handling mechanisms. Ultimately, it enables us to write efficient, maintainable code without sacrificing the advantages of traditional approaches.