Reactive Extensions Basics


The Reactive Extensions (Rx in short) are the libraries provided for a set of programming languages with the aim of simplifying development of asynchronous, event-based applications using streams (observable) and LINQ-style query operators.

Reactive manifesto is a document describing reactive systems and the standards of quality software applications or components and is based on four primary concepts of responsiveness, resilience, elasticity and message driven. The reactive extensions libraries enable developers to easily create reactive programs that cater for those principles in essence.

The following and the rest of the series tries to have a brief overview of the main features of reactive extensions in RxJava.

Observables

Observables are the building blocks of reactive systems. An Observable is an event stream; a sequence of events or data transmitted by a producer (Observable) to a consumer (Observer). It can be used in replace of a List, Iterable, or Stream. Think of an Observable as a pipeline of data flowing from one end to another.

Additionally, the concepts of upstream, downstream, hot and cold observables are repeatedly used and worth mentioning beforehand.

An upstream is the source stream from which we are receiving data while a downstream is the destination stream to which we are sending data.

Cold observables are those that never begin to emit events unless at least one interested party is subscribed to them. This means that each subscriber of a cold Observable receives a full copy of the same stream top to bottom. Hot Observables are those that push the events downstream regardless of any subscription. As a result of this, events may get lost and subscribers might receive the events from various parts of the observable stream.

Core Interfaces

At the core of RxJava, there is the Observable type which represents a stream of data or events to be received by an Observer over time.

1
2
3
interface Observable<T> {
Subscription subscribe(Observer o);
}

An Observer can connect to Observable through Subscription and can receive three kinds of events.

  • Data or events: are pushed via the onNext() method.
  • Exceptions: are pushed via the onError() method.
  • Stream Completion is notified via the onCompleted() method.

After the first exception occurrence or the stream completion, the underlying Observable stream is finished immediately and cannot be reused any more and no further events can be sent over that stream. Accordingly, these events (exception and completion) are called terminal events.
In case of infinite streams, the terminal events might never occur if the stream is infinite and does not fail.

1
2
3
4
5
interface Observer<T> {
void onNext(T t);
void onError(Throwable t);
void onCompleted();
}

The Subscriber wraps the Observer to provide the ability of unsubscribe from the Observable stream.

1
2
3
4
5
6
7
8
interface Subscriber<T> implements Observer<T>, Subscription {
void onNext(T t);
void onError(Throwable t);
void onCompleted();
...
void unsubscribe();
void setProducer(Producer p);
}

Comparison of Non-Reactive & Reactive Systems

Pull vs. Push

In traditional approaches, items are pulled gradually from the source to be processed. However, the main intent of Observable is to push the data or events (Observable can be used with pulling too). In fact, this shift from pull-base to push-base processing could be considered the whole point of RxJava.

Sync vs. Async

Non reactive systems tend to consume source data synchronously while blocking the requesting thread. Reactive components, however, use the fire and forget approach as a result being push-based. Note that an Observable can be used asynchronously or synchronously. They are synchronous by default as they block the subscribing thread and emit events via onNext().

Using callbacks which is a common pattern to achieve asynchronicity has its own few drawbacks including the hassle of coordinating them, their tendency to be nested to create a Christmas tree code as well as sever refactoring a callback-based API into RxJava.

Despite being asynchronous, an Observable stream is always serialized and thread-safe meaning that onNext(), onCompleted() or onError() cannot concurrently execute (e.g. onNext() will not be invoked from different threads).

Eager vs. Lazy

Traditional systems are mainly eager. This means that evaluations will occur regardless of an subscriber’s presence.
In contrast, laziness implies postponing the evaluations and computations until they are certainly asked by a subscriber/client.
This is a very useful feature while performing calculation and stream composition.

Share Comments