Summary:

Stick to whichever library you already have on your CLASSPATH.

If you get a choice, Reactor is preferable, RxJava 2.x is still a good alternative

In case you’re on Android, then RxJava 2.x is your only choice

Table of contents:

API Type-safety Checked exceptions Testing Debugging Spring support Android development Maturity Summary

Flow

Flow

reactive-streams.jar

API

Flowable

Flux

map()

filter()

flatMap()

java.time

java.util.function

import java.time.Duration; ... flux.window(Duration.ofSeconds(1));

import java.util.concurrent.TimeUnit; ... flowable.window(1, TimeUnit.SECONDS);

Duration

CompletableFuture

Optional

java.util.stream.Stream

Type-safety

table

div

RxJava 2 Reactor Purpose Completable N/A Completes successfully or with failure, without emitting any value. Like CompletableFuture<Void> Maybe<T> Mono<T> Completes successfully or with failure, may or may not emit a single value. Like an asynchronous Optional<T> Single<T> N/A Either complete successfully emitting exactly one item or fails. Observable<T> N/A Emits an indefinite number of events (zero to infinite), optionally completes successfully or with failure. Does not support backpressure due to the nature of the source of events it represents. Flowable<T> Flux<T> Emits an indefinite number of events (zero to infinite), optionally completes successfully or with failure. Support backpressure (the source can be slowed down when the consumer cannot keep up)

Completable

Mono<Void>

Mono.then()

Mono<T>

Flux

Observable

Flowable

Flux

Mono

Mono

NoSuchElementException

firstElement()

Maybe

firstOrError()

Single

Flux.next()

Observable

Flowable

Observable

Observable

Flowable

Flowable

Observable

Flux

Checked exceptions

Flux .just("java.math.BigDecimal", "java.time.Instant") .map(Class::forName)

Class.forName()

ClassNotFoundException

java.util.function.Function

Testing

StepVerifier .withVirtualTime(() -> Flux .never() .timeout(ofMillis(100)) ) .expectSubscription() .expectNoEvent(ofMillis(99)) .thenAwait(ofMillis(1)) .expectError(TimeoutException.class) .verify(ofSeconds(1));

TestScheduler

Debugging

Hooks.onOperatorDebug();

import reactor.core.publisher.Flux; import reactor.core.publisher.Hooks; import reactor.core.publisher.Mono; import java.io.File; public class StackTest { public static void main(String[] args) { Mono<Long> totalTxtSize = Flux .just("/tmp", "/home", "/404") .map(File::new) .concatMap(file -> Flux.just(file.listFiles())) .filter(File::isFile) .filter(file -> file.getName().endsWith(".txt")) .map(File::length) .reduce(0L, Math::addExact); totalTxtSize.subscribe(System.out::println); } }

.txt

/tmp

/home

/404

java.lang.NullPointerException at reactor.core.publisher.Flux.fromArray(Flux.java:953) at reactor.core.publisher.Flux.just(Flux.java:1161) at com.nurkiewicz.StackTest.lambda$main$0(StackTest.java:16) at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:368) at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:244) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121) at reactor.core.publisher.FluxArray$ArraySubscription.slowPath(FluxArray.java:126) at reactor.core.publisher.FluxArray$ArraySubscription.request(FluxArray.java:99) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162) at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:229) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90) at reactor.core.publisher.FluxArray.subscribe(FluxArray.java:53) at reactor.core.publisher.FluxArray.subscribe(FluxArray.java:59) at reactor.core.publisher.FluxMapFuseable.subscribe(FluxMapFuseable.java:63) at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:121) at reactor.core.publisher.FluxFilter.subscribe(FluxFilter.java:49) at reactor.core.publisher.FluxFilter.subscribe(FluxFilter.java:53) at reactor.core.publisher.FluxMap.subscribe(FluxMap.java:62) at reactor.core.publisher.MonoReduceSeed.subscribe(MonoReduceSeed.java:65) at reactor.core.publisher.Mono.subscribe(Mono.java:3695) at reactor.core.publisher.Mono.subscribeWith(Mono.java:3801) at reactor.core.publisher.Mono.subscribe(Mono.java:3689) at reactor.core.publisher.Mono.subscribe(Mono.java:3656) at reactor.core.publisher.Mono.subscribe(Mono.java:3603) at com.nurkiewicz.StackTest.main(StackTest.java:23)

NullPointerException

at ...Flux.fromArray() at ...Flux.just() at com.nurkiewicz.StackTest.lambda$main$0(StackTest.java:16) ... at ...FluxArray.subscribe() at ...FluxMapFuseable.subscribe() at ...FluxConcatMap.subscribe() at ...FluxFilter.subscribe() at ...FluxFilter.subscribe() at ...FluxMap.subscribe() at ...MonoReduceSeed.subscribe() ... at com.nurkiewicz.StackTest.main(StackTest.java:23)

Hooks.onOperatorDebug()

Assembly trace from producer [reactor.core.publisher.FluxConcatMap] : reactor.core.publisher.Flux.concatMap(Flux.java:3425) com.nurkiewicz.StackTest.main(StackTest.java:17) Error has been observed by the following operator(s): |_ Flux.concatMap ⇢ com.nurkiewicz.StackTest.main(StackTest.java:17) |_ Flux.filter ⇢ com.nurkiewicz.StackTest.main(StackTest.java:18) |_ Flux.filter ⇢ com.nurkiewicz.StackTest.main(StackTest.java:19) |_ Flux.map ⇢ com.nurkiewicz.StackTest.main(StackTest.java:20) |_ Flux.reduce ⇢ com.nurkiewicz.StackTest.main(StackTest.java:21)

NullPointerException

Spring support

Mono

Flux

Mono

DeferredResult

Android development

avoid callback hell by modelling UI events as streams

easily switching back and forth between threads, especially making sure I/O doesn’t happen on UI thread

Maturity

Summary

timeout(100)