RxJS

wprowadzenie do programowania reaktywnego

Rodzaje asynchroniczności w aplikacja webowych

  • AJAX
  • DOM Events
  • animacje
  • WebSocket data
  • WebWorkers results
  • setInterval

Przykład

getData(query, (data) => {
    console.log('data received', data);
});

Problemy z asynchronicznością

getData(query, (data1) => {
    console.log('data1 received', data1);
    getData(data1.id, (data2) => {
        console.log('data2 received', data2);
        getData(data2.url, (data3) => {
            console.log('data3 received', data3);
                getData(data3.name, (finalData) => {
                console.log('finalData received', finalData);
                finalStuff = doStuff(finalData);
            });
        });
    });
});

callback hell

aka "flying V"

Rozwiązanie - Promises

let finalStuff = getData(query)
    .then(data1 => {
        console.log('data1 received', data1);
        return getData(data1.id);
    })
    .then(data2 => {
        console.log('data2 received', data2);
        return getData(data2.url);
    })
    .then(data3 => {
        console.log('data3 received', data3);
        return getData(data3.name);
    })
    .then(finalData => {
        console.log('finalData received', finalData);
        return doStuff(finalData);
    })

Rozwiązanie - Promises

Pros

  • synchroniczna reprezentacja asynchroniczności
  • immutable

reprezentacja wartości z przyszłości

Promises

let finalStuff = getData(query)
    .then(getOtherData)
    .then(getAnotherOne)
    .then(doFinallStuff)

Rozwiązanie - Promises

Pros

  • synchroniczna reprezentacja asynchroniczności
  • immutable

Cons

  • tylko AJAX jest pojedynczą wartością

reprezentacja wartości z przyszłości

Rodzaje asynchroniczności w aplikacja webowych

  • AJAX
  • DOM Events
  • animacje
  • WebSocket data
  • WebWorkers results
  • setInterval

Rozwiązanie - Promises

Pros

  • synchroniczna reprezentacja asynchroniczności
  • immutable

Cons

  • tylko AJAX jest pojedynczą wartością
  • nie można anulować (a AJAXa można)

reprezentacja wartości z przyszłości

Observable

Observable

Synchronous Asynchronous
single value Object Promise
multiple values Iterables
(Array / Set / Map)
Observable

Observable

  • strumienie / zbiory
  • zdarzenia jak kolekcje
  • immutable
  • lazy
  • można anulować (przykład)

dowolna ilość obiektów na przestrzeni dowolnego czasu

Observable

strumień

Observable

operator

Observable

operatory

audit, auditTime, buffer, bufferCount, bufferTime, bufferToggle, bufferWhen,
cache, catch, combineAll, combineLatest, concat, concatAll, concatMap,
concatMapTo, count, debounce, debounceTime, defaultIfEmpty, delay, delayWhen,
dematerialize, distinct, distinctKey, distinctUntilChanged,
distinctUntilKeyChanged, do, elementAt, every, exhaust, exhaustMap, expand,
filter, finally, find, findIndex, first, groupBy, ignoreElements, isEmpty, last,
let, map, mapTo, materialize, max, merge, mergeAll, mergeMap, mergeMapTo, mergeScan,
min, multicast, observeOn, onErrorResumeNext, pairwise, partition, pluck, publish,
publishBehavior, publishLast, publishReplay, race, reduce, repeat, repeatWhen, retry,
retryWhen, sample, sampleTime, scan, sequenceEqual, share, single, skip, skipUntil, 
skipWhile, startWith, subscribeOn, switch, switchMap, switchMapTo, take, takeLast,
takeUntil, takeWhile, throttle, throttleTime, timeInterval, timeout, timeoutWith,
timestamp, toArray, toPromise, window, windowCount, windowTime, windowToggle,
windowWhen, withLatestFrom, zip, zipAll

Observable

zdarzenia jak kolekcje

let iterable = [0, 1, 1, 2, 3, 5, 8, 13];

iterable
    .filter(x => x % 2 === 0)
    .map(x => x + x)
    .forEach(x => console.log("x: ", x));
observable
    .filter(x => x % 2 === 0)
    .map(x => x + x)
    .forEach(x => console.log("x: ", x));

RxJS - LoDash for async

Observable

tworzenie observable

var source = Rx.Observable.create(function (observer) {
    observer.onNext(42);
    observer.onCompleted();

    // Note that this is optional, you do not have to return this if you require no cleanup
    return function () { console.log('disposed'); };
});
var source = Rx.Observable.generateWithRelativeTime(
    1,
    x => x < 100,
    x => x + 1,
    x => x,
    x => 100 * x
).timeInterval();

var subscription = source.subscribe(
    x => console.log('Next: ', x),
    x => console.log('Error: ', x),
    x => console.log('Completed')
);

Observable

tworzenie observable

let fromArray = Rx.Observable.from([1, 2, 3, 4, 5, 6]);
let map = new Map([[1, 2], [2, 4], [4, 8]]);
let fromMap = Rx.Observable.from(m);
let fromStr = Rx.Observable.from("abcdefgh");
let fromInterval = Rx.Observable.interval(500).timeInterval().take(3);
let justTwo = Rx.Observable.just(2);
let zeroToThree = Rx.Observable.range(0, 3);
let threeTimes = Rx.Observable.repeat(42, 3);
let empty = Rx.Observable.empty();
let never = Rx.Observable.never();
let throw = Rx.Observable.throw();

Observable

tworzenie observable

let inputKeydowns = Rx.Observable.fromEvent(input, 'keydown');
let fromPromise = Rx.Observable.startAsync(function () {
    return RSVP.Promise.resolve(42);
});
let fromAJAX = Rx.DOM.ajax({
    method: 'GET',
    url: 'domain.com/url',
    responseType: 'json'
});
function initialize() {}
let ready = Rx.DOM.ready().subscribe(initialize);
let socket = Rx.DOM.fromWebSocket(
  'ws://echo.websocket.org',
  null, // no protocol
  openObserver,
  closingObserver);

Observable

bindings

  • DOM binding,
  • AJAX,
  • jQuery,
  • nodeJS,
  • Angular HTTP/Realtime Data services,
  • React Stores,
  • Ember computed property,
  • etc, etc,

Observable

anulowanie zapytań - autocomplete

// search input
var q = document.querySelector('#q');
// serch suggestions container
var resultList = document.querySelector('#results');

// stream of keyups on the input
var keyups = Rx.Observable.fromEvent(q, 'keyup');

keyups
  // Throttle events to at most one on 0,5s
  .throttle(500)
  // transform to stream of values
  .map(() => q.value)
  // side effect - show the spinner next to input
  .do(() => q.classList.add('spinner'))
  // transform every query to Ajax stream an map every for one stream with latest emit item
  .flatMapLatest(query => Rx.DOM.ajax({
    method: 'GET',
    url: '/autocomplete?q=' + query,
    responseType: 'json'
  }))
  // async is over - hide the spinner
  .do(() => q.classList.remove('spinner'))
  // get results from each response
  .map(r => r.response)
  // decorate results with html and join to one
  .map(results => results.reduce((html, result) => `${html}<li>${result}</li>`, ''))
             // success - insert result to the DOM
  .subscribe(resultsHTML => resultList.innerHTML = resultsHTML,
             // fail
             err => console.error(err));

Observable

łączenie strumieni - drag'n'drop

const { fromEvent } = Rx.Observable;
const target = document.querySelector('.box');

const mousedown = fromEvent(target, 'mousedown');    // ---o-------------------------------------->
const mouseup = fromEvent(target, 'mouseup');        // -----------------------------o------------>
const mousemove = fromEvent(document, 'mousemove');  // -ooooo----ooo----oo---o--o-------ooo---o-->

// mouse drag event stream is every mouse move during mousedown until mouseup happen
const mousedrag = mousedown.flatMap((md) => {
  const startX = md.clientX + window.scrollX,
        startY = md.clientY + window.scrollY,
        startLeft = parseInt(md.target.style.left, 10) || 0,
        startTop = parseInt(md.target.style.top, 10) || 0;

  return mousemove.map((mm) => {   // ---(1,2)-(1,3)-(2,6) --->
    mm.preventDefault();

    return {
      left: startLeft + mm.clientX - startX,
      top: startTop + mm.clientY - startY
    };
  }).takeUntil(mouseup);                             // ----oo----ooo----oo---o--o---------------->
});

Reactive Extensions

  • RxJS v.4.1 - stable
    •  
  • RxJS v.5.0.0-beta.12
    •  
    • wsparcie od IE10 (IE9?)
    • w całości przepisane w ES6
    • większa modularność (modułowość?)
    • poprawiona wydajność (~4.6x)
github.com/Reactive-Extensions/RxJS
github.com/ReactiveX/rxjs

Reactive Extensions

RxLua, Rx.rb, RxPy,

RxGroovy, RxJRuby, RxPHP

Dziękuję

Rx Dokumentacja - z przykładami dla każdego języka

http://reactivex.io/intro.html

RxDOM - binding do drzewa DOM

https://github.com/Reactive-Extensions/RxJS-DOM

Ben Lesh - RxJS Version 5

http://bit.ly/BenLeshRxJS5

Jafar Husain - ES7: The Evolution of JavaScript

http://bit.ly/es2016evo

Jafar Husain - Async JavaScript with Reactive Extensions

http://bit.ly/asyncWithRx
Made with Slides.com