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=πr2A_{circle} = \pi r^2
A_{segment} = {\pi r^2 \over 4}
Asegment=πr24A_{segment} = {\pi r^2 \over 4}
r
rr
A_{square} = r^2
Asquare=r2A_{square} = r^2
x
xx
y
yy
d = \sqrt{x^2 + y^2}
d=x2+y2d = \sqrt{x^2 + y^2}
d
dd
\pi \approx 4 \times {|points_{d \leq r}| \over |points|}
π4×pointsdrpoints\pi \approx 4 \times {|points_{d \leq r}| \over |points|}
\approx {|points_{d \leq r}| \over |points|}
pointsdrpoints\approx {|points_{d \leq r}| \over |points|}
{A_{segment} \over A_{square}} = {\pi \over 4}
AsegmentAsquare=π4{A_{segment} \over A_{square}} = {\pi \over 4}
\Leftrightarrow
\Leftrightarrow

Reactive Programming

By Rob Bosman

Reactive Programming

Using the Rx-ified API of Vert.x

  • 97