Reactive Programming


var a = 1;
var b = a + 1;
a = 5;
print(b); // 2
doPeriodic(1000,
function() {
if (getTimeInSeconds() % 2 == 0) {
playSound("TIK.mp3");
} else {
playSound("TAK.mp3");
}
});
print("Bye!");imperative
functional
reactive
programming paradigms
Message msg = new Message();
msg.setFrom("alice@email.com");
msg.setTo("bob@email.com");
msg.setSubject("Lees dit!");
msg.setBody("Wie dit leest is gek.");
MailServer.send(msg);public class Message {
private String from;
private String to;
private String subject;
private String body;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}public class MailServer {
public static void send(Message msg) {
...
}
}
Message msg = new Message();
msg.setFrom("alice@email.com");
msg.setTo("bob@email.com");
msg.setSubject("Lees dit!");
msg.setBody("Wie dit leest is gek.");
MailServer.send(msg);
msg.setTo("claire@email.com");
MailServer.send(msg);public class MailServer {
public static void send(MessageFiller msgFiller) {
...
}
}
public interface MessageFiller {
public abstract void fill(Message msg);
}
public class Message {
private String from;
private String to;
private String subject;
private String body;
public String getFrom() {
return from;
}
public Message setFrom(String from) {
this.from = from;
return this;
}
public String getTo() {
return to;
}
public Message setTo(String to) {
this.to = to;
return this;
}
public String getSubject() {
return subject;
}
public Message setSubject(String subject) {
this.subject = subject;
return this;
}
public String getBody() {
return body;
}
public Message setBody(String body) {
this.body = body;
return this;
}
}
MailServer.send(new MessageFiller() {
@Override
public void fill(Message msg) {
msg.setFrom("alice@email.com");
msg.setTo("bob@email.com");
msg.setSubject("Lees dit!");
msg.setBody("Wie dit leest is gek.");
}
});
MailServer.send(new MessageFiller() {
@Override
public void fill(Message msg) {
msg.setFrom("alice@email.com");
msg.setTo("bob@email.com");
msg.setSubject("Lees dit!");
msg.setBody("Wie dit leest is gek.");
}
});
MailServer.send(msg -> {
msg.setFrom("alice@email.com");
msg.setTo("bob@email.com");
msg.setSubject("Lees dit!");
msg.setBody("Wie dit leest is gek.");
});
MailServer.send(new MessageFiller() {
@Override
public void fill(Message msg) {
msg.setFrom("alice@email.com");
msg.setTo("bob@email.com");
msg.setSubject("Lees dit!");
msg.setBody("Wie dit leest is gek.");
}
});
MailServer.send(msg -> {
msg.setFrom("alice@email.com");
msg.setTo("bob@email.com");
msg.setSubject("Lees dit!");
msg.setBody("Wie dit leest is gek.");
});
MailServer.send(msg -> msg
.setFrom("alice@email.com")
.setTo("bob@email.com")
.setSubject("Lees dit!")
.setBody("Wie dit leest is gek."));public class MailServer {
public static void send(MessageFiller msgFiller) {
...
}
}
@FunctionalInterface
public interface MessageFiller {
public abstract void fill(Message msg);
}
List<Person> persons = ...;
for (Person person : persons) {
if (person.getBusinessUnit() == SALES) {
for (String mailAddress : person.getMailAddresses()) {
if (mailAddress.endsWith("@email.com")) {
MailServer.send(msg -> msg
.setFrom("alice@email.com")
.setTo(mailAddress)
.setSubject("Lees dit!")
.setBody("Wie dit leest is gek."));
}
}
}
}
List<Person> persons = ...;
for (Person person : persons) {
if (person.getBusinessUnit() == SALES) {
for (String mailAddress : person.getMailAddresses()) {
if (mailAddress.endsWith("@email.com")) {
MailServer.send(msg -> msg
.setFrom("alice@email.com")
.setTo(mailAddress)
.setSubject("Lees dit!")
.setBody("Wie dit leest is gek."));
}
}
}
}
persons.stream()
.filter(person -> person.getBusinessUnit() == SALES)
.flatMap(salesPerson -> salesPerson.getMailAddresses())
.filter(salesMailAddress -> salesMailAddress.endsWith("@email.com"))
.foreach(salesEmailDotComAddress -> MailServer.send(msg -> msg
.setFrom("alice@email.com")
.setTo(salesEmailDotComAddress)
.setSubject("Lees dit!")
.setBody("Wie dit leest is gek.")));



web
socket
client
server
event bus
event bus
package nl.bransom.reactive;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
public class Main {
public static void main(String[] args) {
final Vertx vertx = Vertx.vertx();
final Future<String> whenRainMakerIsDeployed = Future.future();
final Future<String> whenRainServerIsListening = Future.future();
vertx.deployVerticle(RainMaker.class.getName(), whenRainMakerIsDeployed.completer());
vertx.deployVerticle(RainServer.class.getName(), whenRainServerIsListening.completer());
CompositeFuture.all(whenRainMakerIsDeployed, whenRainServerIsListening)
.setHandler(result -> {
if (result.succeeded()) {
vertx.eventBus().publish(
"rain.intensity.set",
new JsonObject().put("value", 0.3));
}
});
}
}package nl.bransom.reactive;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
public class Main {
public static void main(String[] args) {
final Vertx vertx = Vertx.vertx();
vertx.deployVerticle(RainMaker.class.getName());
vertx.deployVerticle(RainServer.class.getName(), result -> {
if (result.succeeded()) {
vertx.eventBus().publish(
"rain.intensity.set",
new JsonObject().put("value", 0.3));
}
});
}
}To show the real power of Reactive, let's just say that you want to have a stream of "double click" events. To make it even more interesting, let's say we want the new stream to consider multiple clicks (two or more) as double clicks.


package nl.bransom.reactive;
import io.vertx.core.json.JsonObject;
import io.vertx.rxjava.core.AbstractVerticle;
import io.vertx.rxjava.core.eventbus.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import rx.Subscriber;
public class RainMaker extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(RainMaker.class);
@Override
public void start() {
vertx.eventBus()
.<JsonObject>consumer("rain.intensity.set")
.toObservable()
.map(Message::body)
.map(jsonObject -> jsonObject.getDouble("value"))
.map(RainMaker::intensityToIntervalMillis)
.switchMap(this::createRainDropObservable)
.map(RainDrop::toJson)
.subscribe(
rainDropJson -> vertx.eventBus().publish("rain.drop.notify", rainDropJson),
throwable -> LOG.error("Error making rain.", throwable));
}
private static long intensityToIntervalMillis(final double intensity) {
final double effectiveIntensity = Math.min(Math.max(0.0, intensity), 1.0);
LOG.debug("intensity: {}", effectiveIntensity);
return Math.round(Math.pow(Math.E, Math.log(3000) * (1.0 - effectiveIntensity)));
}
private Observable<? extends RainDrop> createRainDropObservable(final long intervalMillis) {
LOG.debug("intervalMillis: {}", intervalMillis);
if (intervalMillis < 3000) {
return Observable.<RainDrop>create(subscriber -> createDelayedRainDrop(intervalMillis, subscriber));
} else {
return Observable.empty();
}
}
private void createDelayedRainDrop(final long intervalMillis, final Subscriber<? super RainDrop> subscriber) {
final long delayMillis = sampleDelayMillis(intervalMillis);
vertx.setTimer(delayMillis, timerId -> {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(new RainDrop());
createDelayedRainDrop(intervalMillis, subscriber);
}
});
}
private static long sampleDelayMillis(final long intervalMillis) {
return Math.max(1, Math.round(2.0 * new Random().nextDouble() * intervalMillis));
}
}Rain demo
Monte Carlo
A_{circle} = \pi r^2
Acircle=πr2
A_{segment} = {\pi r^2 \over 4}
Asegment=4πr2
r
r
A_{square} = r^2
Asquare=r2
x
x
y
y
d = \sqrt{x^2 + y^2}
d=x2+y2
d
d
\pi \approx 4 \times {|points_{d \leq r}| \over |points|}
π≈4×∣points∣∣pointsd≤r∣
\approx {|points_{d \leq r}| \over |points|}
≈∣points∣∣pointsd≤r∣
{A_{segment} \over A_{square}}
= {\pi \over 4}
AsquareAsegment=4π
\Leftrightarrow
⇔
Reactive Programming
By Rob Bosman
Reactive Programming
Using the Rx-ified API of Vert.x
- 97