Reactive Programming with RxJS

Aliaksei Bahachuk 

Reactivity is modern, but What is it?

Reactive Programming

Programming paradigm oriented around data flows and the propagation of change (wiki)

Observables + LINQ + Schedulers (Microsoft)

Programming paradigm with asynchronous data streams (Andre Staltz)

Reactive Programming

a := 1;
b := 2;

c := a + b; //c === 3

Reactive Programming

c := a + b; //c === 3

a := 3 // c === ???

Reactive Programming

a := 1;
b := 2;

c := a + b; //c === 3

a := 3; //c === 5

Cell

  • type Cell(a) = t -> a

  • Behaviour/signal/cell

Data Flow

  • type Stream(a) = [(t, a)]

Everything can be a stream

  • Events

  • Variables

  • Data Structures

  • You can actually model every input/output that happens in a browser as a stream (or a stream of streams)

Everything can be a stream

Can we, effectively, build a program using this notion?

Meet your new friend:

 The Observable

Your friendly neighbor: RxJS

Reactive Extensions

Are a set of libraries for composing asynchronous and event-based programs using observable sequences and fluent query operators

Rx is everywhere

Java

JavaScript

.Net

Ruby

Swift

C++

Clojure

Scala

What is the most asynchronous?

User

More async data streams

  • XHR requests

  • WebSocket

  • Promises

  • Events

  • Dom Inputs

  • Web Worker

  • Web RTC

RxJS operators

RxJS operators

RxJS operators: map

resStream

RxJS operators: delay

resStream

RxJS operators: debounce

resStream

RxJS operators: merge

resStream

RxJS operators: combineLatest

resStream

RxJS operators: zip

resStream

Let's create a morse-code decoder!

Morse-code decoder

click...

click...

click.............

click...

Morse-code decoder

click...

click...

click.............

click...

Morse-code decoder

  • Dom events (keydown, keyup)

  • Start signal/End signal

  • Dot/Dash/Whitespace

  • Letter

  • Word/Sentence

Morse-code decoder

Morse-code decoder

  • "." = ~400ms

  • "_" = 3 * "."

  • between codes = "."

  • between letters = 3 * "." 

  • between words = 7 * "."

Morse-code decoder

const keyUps = Rx.Observable
                 .fromEvent(document, 'keyup');

const keyDowns = Rx.Observable
                   .fromEvent(document, 'keydown');

Morse-code decoder

const spaceKeyUps = keyUps.filter((data) => data.keyCode === 32);

const spaceKeyDowns = keyDowns.filter((data) => data.keyCode === 32);

Morse-code decoder

const signalStartsRaw = spaceKeyDowns.map(() => "start");
const signalEndsRaw = spaceKeyUps.map(() => "end");


const signalStartsEnds = Rx.Observable
                            .merge(signalStartsRaw, signalEndsRaw)
                            .distinctUntilChanged();

Morse-code decoder

const signalStarts = signalStartsEnds
                           .filter((ev) => ev === "start")
                           .timestamp();
const signalEnds = signalStartsEnds
                           .filter((ev) => ev === "end")
                           .timestamp();



const spanStream = signalStarts.flatMap((start) => {
    return signalEnds.map((end) => end.timestamp - start.timestamp)
                     .first();
});

Morse-code decoder

Morse-code decoder

const SPAN = 400;

const dotsStream = spanStream
                        .filter((v) => v <= SPAN)
                        .map(() => ".");

const lineStream = spanStream
                        .filter((v) => v > SPAN)
                        .map(() => "-");

Morse-code decoder

const dotsAndLines = Rx.Observable.merge(dotsStream, lineStream);

// [['.', '.', '-'], ['-', '.', '-'] ... ] 
const letterCodes = dotsAndLines.buffer(letterWhitespaces); 

 // ['A', 'B' ...]
const lettersStream = letterCodes
                        .map((codes) => morse.decode(codes.join("")));

Morse-code decoder

Morse-code decoder

What else?

Component bindings

Component bindings

click...

click...

click.............

click...

click

category.

comp.

bundle.

package.

Component bindings

Component bindings

Problems RxJS solves

  • Data Manipulation

  • Events handling

  • Callback Hell

  • Promise Hell

  • Comet/Async Manipulation

  • Race Conditions

  • Complex State

  • Tests 

RxJS problems

Complexity

Integration with imperative part of code

Documentation

Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences or Promises or array/iterable into one observable sequence.

Cascade Updating

Cascade Updating

Cascade Updating/Exceptions

RxJS with other frameworks

RxJS with Angular - rx.angular.js

angular.module('example', ['rx'])
  .controller('AppCtrl', function($scope, $http, rx) {

    function searchWikipedia (term) {
      return rx.Observable
        .fromPromise($http({}))
        .map(function(response){ return response.data[1]; });             
    }

    $scope.search = '';
    $scope.results = [];

    $scope.$createObservableFunction('click')
      .map(function () { return $scope.search; })
      .flatMapLatest(searchWikipedia)
      .subscribe(function(results) {
        $scope.results = results;
      });
  });

RxJS with Angular2 - inside

RxJS with React - rx-react

RxJS with others

Alternative?

Bacon

Kefir

Highland

I want more hardcore!

Javelin and ClosureScript

(defc test-results
  {:scores [74 51 97 88 89 91 72 77 69 72 45 63]
   :proctor "Mr. Smith"
   :subject "Organic Chemistry"
   :sequence "CHM2049"})

(defc= test-results-with-mean
  (let [scores (:scores test-results)
        mean   (/ (reduce + scores) (count scores))
        grade  (cond (<= 90 mean) :A
                     (<= 80 mean) :B
                     (<= 70 mean) :C
                     (<= 60 mean) :D
                     :else        :F)]
    (assoc test-results :mean mean :grade grade)))

Cycle.js

  • RxJS

  • Side effects/Logic

  • Composition

  • Streams, streams, streams, streams

Questions?

Reactive Programming with RxJS

By Aliaksei Bahachuk

Reactive Programming with RxJS

An overview of RxJS

  • 1,156