An API / library for composing asynchronous and event driven programs by using observable sequences
A new idea/paradigm in programming
Ported in more than 10 languages
RxJava ported by Netflix
based on the principle of non-blocking push rather than pull
An easy and scalable way to propagate all data changes to different parts/layers of our app that may want to react on them in parallel
(Pub-sub pattern):
No clear connection between event producer and event consumer
Difficult to track your code logic
Difficult to test
Single data pipeline and a ton of XXXEvent Classes
(mixes blocking and non-blocking approach into one API ):
More oriented to asynchronously running of tasks with side effects or returning single result
Complex and non intuitive for asynchronous processing of streams
Limited api and composition capabilities
(a combination of the Observer pattern, the Iterator pattern, and functional programming)
single item | multiple items | |
---|---|---|
synchronous | T getData() | Iterable<T> getData |
asynchronous | Future<T> getData() | Observable<T> getData() |
Observable(source of data stream, Observer(subscribes/listens/reacts)
event | iterable(pull) | Observable(push) |
---|---|---|
retrieve data | T next() | onNext(T) |
discover error | throws Exception | onErron(Exception) |
complete | !hasNext() | onCompleted() |
Takes reactive programming to the next level
Functional & reactive programing - asynchronous data streams on steroids:
Easily create data streams of anything, i.e. variables, user inputs, properties, caches, data structures. Data streams are cheap and ubiquitous.
Apply functional composition to the data stream, i.e., have an amazing toolbox of functions to combine, map, filter, retry, throttle, any of those streams and an easy way to create new operators.
Easy threading management (abstract away low-level threading issue)
Easy async error handling
More declarative, less side-effects and less mutable state
Higher manipulation, reusability, modularity of code and objects
Observable.just(1, 2, 3).toBlocking().subscribe(System.out::println);
Observable.range(100, 120).toBlocking().subscribe(System.out::println);
Observable.fromCallable(() -> 10).toBlocking().subscribe(System.out::println);
Observable.from(ImmutableList.of(1, 2, 3)).toBlocking().subscribe(System.out::println);
Observable.from(new Integer[]{ 1, 2, 3 }).toBlocking().subscribe(System.out::println);
Observable.form(future)...
Observable.create(subscriber -> {
try {
if (subscriber.isUnsubscribed()) {
return;
}
subscriber.onNext(1000);
subscriber.onCompleted();
} catch (Exception e) {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(e);
}
}
}).toBlocking().subscribe(System.out::println);
Observable<Doc> getObservableDoc(String docName) {
return Observable.create(subscriber -> {
subscriber.onNext(esClient.getDoc(docName));
subscriber.onCompleted();
}
}
Observable
.from(ImmutableList.of(“doc1”, “doc2”, “doc3”))
.flatMap(docName -> getObservableDoc(docName))
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.subscribe(document -> System.out.println(document::getTitle));
Observable
.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.filter(item -> item % 2 == 0)
.scan((sum, item) -> sum + item)
.last()
.subscribe(System.out::println);
Observable
.just("cat", "dog", "ant", "bee")
.groupBy(animal -> {
if (animal.equals("cat") || animal.equals("dog") {
return "mammal";
} else {
return "insect";
}
})
.subscribe(groupObservable -> {
groupObservable.toList()
.subscribe(itemInGroup -> System.out.println("Group " +
groupObservable.getKey() +
" items: " + itemsInGroup));
});
public class RxBus {
private final Subject<Object, Object> rxBus =
PublishSubject.create().toSerialized();
public void send(Object event) {
rxBus.onNext(event);
}
public Observable<Object> toObservable() {
return rxBus;
}
}
rxBus.toObservable().subscribe(event -> processEvent(event))
public static <T> CompletableFuture<List<T>> fromObservable(Observable<T> observable) {
final CompletableFuture<List<T>> future = new CompletableFuture<>();
observable
.doOnError(future::completeExceptionally)
.toList()
.forEach(future::complete);
return future;
}
public static <T> Observable<T> toObservable(CompletableFuture<T> future) {
return Observable.create(subscriber ->
future.whenComplete((result, error) -> {
if (error != null) {
subscriber.onError(error);
} else {
subscriber.onNext(result);
subscriber.onCompleted();
}
})
);
}
Completable task1 = Completable
.fromAction(() -> System.out.println("Task 1 completed"))
.subscribeOn(Schedulers.io());
Completable task2 = Completable
.fromAction(() -> System.out.println("Task 2 completed"))
.subscribeOn(Schedulers.io());
Completable.merge(task1, task2).subscribeOn(Schedulers.io()).await();
distinct, elementAt, filter, find, first, last, pausable, skip, skipUntil, take, takeUntil
average, count, max, min, reduce, sum
every, some, includes, sequenceEqual
delay, findIndex, map, scan, debounce
Observable.merge(o1, o2, ...)
1---2---3---4------|
------5--------6---|
1---2-5-3---4--6---|
Observable.compineLatest(List<Observable<T>>, (List<T>) -> {})
--1------2---------------3--4----5
------A-----B------C-D------------
--1A-----2A-2B----2C-2D--3D-4D---5D
Observable.concat(o1, o2)
---1------1--------------1---
--2---2--
---1------1--------------1---2---2--
Observable.just(1, 2, 3).withLatestFrom(ob2, (x, y) -> x + y);
Observable.zip(List<Observable<T>>, (List<T>) -> {})
Observable.amp(o1, o2); // emit all items from the first to emit
Observable<String> numbers = Observable
.just("1", "2", "three", "4", "5")
.map(Integer::parseInt)
.onErrorReturn(e -> -1)
.subscribe(System.out::println);
Observable<Integer> defaultOnError =
Observable.just(5, 4, 3, 2, 1);
Observable<String> numbers = Observable
.just("1", "2", "three", "4", "5")
.map(Integer::parseInt)
.onExceptionResumeNext(defaultOnError);
Observable.just(1, 2, 3, 4, 5, 6)
.timeout(100, TimeUnit.MILLISECONDS)
.retryWhen(failedAttempts -> {
return failedAttempts
.zipWith(Observable.just(1, 5, 10), (failedAttempt, wait) -> wait)
.flatMap(wait -> Observable.timer(wait, TimeUnit.SECONDS));
})
.subscribe(System.out::println);
Observable
.sample(Observable
.interval(100L, TimeUnit.MILLISECONDS)
.take(10)
.concatWith(Observable
.interval(200L, TimeUnit.MILLISECONDS))
)
.debounce(150L, TimeUnit.MILLISECONDS)
.onBackpressureBuffer(10000)
.onBackpressureDrop()
.window(3L, 200L, TimeUnit.MILLISECONDS);
Observable<DeployableState> observableDeploy = Observable.create(subscriber -> {
subscriber.onNext(deployer.deploy())
subscriber.onComplete()
}
.concatWith(
Observable.interval(5, TimeUnit.SECOMDS, Schedulers.io())
.map(tick -> {
if ((tick + 1L) * 5) > maxWaitTimeSeconds) {return TIMED_OUT}
return watcherFuction.apply(deployRequest, deployable)
}
.lift(new OperatorTakeWhileInclusive(watchedState -> !watchedState.isTerminalState)));
)
.onErrorReturn(throwable -> FAILED_TO_ACTIVATE)
.subcribeOn(Schedulers.io())
.observeOn(Schedulers.computation());
Observable<Deployable> getObservableOfCompinedDeployStates(
List<Observable<DeployableState>> childObservableDeploys) {
return Observable.combineLatest(childObservableDeploys, (latestStateList) -> {});
};
Observable<Integer> obj = Observable.just(1, 2, 3).publish()
// Observable<Integer> obj = Observable.just(1, 2, 3).replay()
// Observable<Integer> obj = Observable.just(1, 2, 3).publish().refCount()
Subscription sub1 = obj.subscribe(System.out::println);
Subscription sub2 = obj.subscribe(System.out::println);
obj.connect();
http://reactivex.io/tutorials.html
https://github.com/ReactiveX/RxJava/wiki
https://github.com/ReactiveX/RxNetty
https://github.com/davidmoten/rxjava-jdbc