Slide 1

Slide 1 text

Introduction to Reactive Extensions without saying “functional” or “reactive” Tetsuharu OHZEKI (April 28, 2016. html5j platform)

Slide 2

Slide 2 text

About me  Tetsuharu OHZEKI  twitter: saneyuki_s  Open Source Developer  Mozilla Committer (Firefox, Servo as reviewer)  ReactiveX/RxJS  And some others… (whatwg html, etc)  Working at VOYAGE GROUP, inc.  My main work is to develop an advertising management system with using TypeScript, babel compiler, React, and RxJS.

Slide 3

Slide 3 text

https://youtu.be/COviCoUtwx4

Slide 4

Slide 4 text

I don’t talk about…  Functional Programming  Functional Reactive Programming (FRP)  Fiber-Reinforced Plastics (FRP)  Enterprise Application Architecture Pattern  Read “Microsoft .NET - Architecting Applications for the Enterprise, 2nd Edition (Dino Esposito)”. It’s very nice.  Recommend to use RxJS

Slide 5

Slide 5 text

It is sometimes called “functional reactive programming” but this is a misnomer. ReactiveX may be functional, and it may be reactive, but “functional reactive programming” is a different animal. One main point of difference is that functional reactive programming operates on values that change continuously over time, while ReactiveX operates on discrete values that are emitted over time. http://reactivex.io/intro.html

Slide 6

Slide 6 text

I’ll talk about… Programming style for application engineering in practice

Slide 7

Slide 7 text

const subject = new SomeEventEmitter(); const flag1 = false; const flag2 = Date.now(); const flag3 = false; subject.on('bar', (e) => { flag1 = !flag1; }); subject.on('foo', (e) => { const now = Date.now(); if (now > (flag2 + 100)) { flag2 = now; flag3 = true; } }); subject.on('hogefuga', (e) => { if (flag1 && flag3) { alert('hello!'); } });

Slide 8

Slide 8 text

Events are everywhere…  Data Mutation  Domain Event  File I/O  Network I/O  Network Status  User Input  Geolocation  Battery  Memory Pressure  Device Orientation  Ambient light  Cluster Node’s status  Server monitoring  Logging

Slide 9

Slide 9 text

Events are queryable collection  If we regard an event stream as a sequenced data structure, we can regard a set of them as a collection.  Collection, we can query it.

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Rx came from C#…  Originated in “Microsoft Live Labs Volta” (2008)  Designed by Erik Meijer and his team  Reactive Extensions in .NET (2009~)  “LINQ to Events”  RxJava is open sourced by Netflix (2012)  By Jefer Husain https://twitter.com/jhusain https://vimeo.com/110554082

Slide 12

Slide 12 text

Rx key components Observable Operator Subject Scheduler

Slide 13

Slide 13 text

Pull Push One T Promise Zero- Infinity Iterable Observable Observable

Slide 14

Slide 14 text

Operator http://reactivex.io/documentation/operators/map.html

Slide 15

Slide 15 text

Subject http://reactivex.io/documentation/subject.html

Slide 16

Slide 16 text

Scheduler Event Loop Scheduler

Slide 17

Slide 17 text

Programming Key Points  Repeat these until you get an expected final value 1. Define a “source” of an event as Observable 2. Apply Operator, and create a new “check-point” Observable  Scheduler can control “timing” to deliver a value

Slide 18

Slide 18 text

Compose a new operator for you // Filter sequence, then transform from T to U. function filterMap(source: Rx.Observable, filterFn: (v: T) => boolean, mapFn: (v: T) => U): Rx.Observable{ return source.filter(filterFn).map(mapFn); } const foo = filterMap(bar);

Slide 19

Slide 19 text

const subject = new SomeEventEmitter(); const flag1 = false; const flag2 = Date.now(); const flag3 = false; subject.on('bar', function(e) { flag1 = !flag1; }); subject.on('foo', function(e) { const now = Date.now(); if (now > (flag2 + 100)) { flag2 = now; flag3 = true; } }); subject.on('hogefuga', function(e) { if (flag1 && flag3) { alert('hello!'); } });

Slide 20

Slide 20 text

const bar = Observable.fromEvent(subject, 'bar') .scan((flag) => !flag, false) .filter((ok) => ok); const foo = Observable.fromEvent(subject, 'foo') .debounce(100); Observable.fromEvent(subject, 'hogefuga') .withLatestFrom(bar, foo, () => alert('hello!'));

Slide 21

Slide 21 text

alert() withLatestFrom filter scan bar debounce (100) foo barfoo

Slide 22

Slide 22 text

See the implementation: scan()

Slide 23

Slide 23 text

See the implementation: scan https://github.com/ReactiveX/rxjs/blob/a3ec896/src/operator/scan.ts

Slide 24

Slide 24 text

alert() WithLatestFromSubscriber FilterSubscriber ScanSubscriber bar DebounceSubscriber foo barfoo

Slide 25

Slide 25 text

Rx is just “advanced” observer pattern  Sort interfaces & calling convention  Define many middle-relay object as “Operator”

Slide 26

Slide 26 text

Pull Push Zero- Infinity Iterable Observable

Slide 27

Slide 27 text

Duality (Iterable vs Observable) Pull (e.g. Iterable) Calling “ancestor” data source from “descendant” consumer. Push (e.g. Observable) Calling “descendant” consumer from “ancestor” data source data source consumer Pull Push

Slide 28

Slide 28 text

e.g. Iterable + operator extensions (Let’s create!) const exiterable = ExIterable.create(iterable); const result = exiterable .filter((v) => v % 2 === 0) .map((v) => v * 2); const {done, value} = result.next(); for (let value of result) { … } // caution: blocking

Slide 29

Slide 29 text

e.g. Iterable -> Observable (Sync) // This push all values as sync const iterableToObservable = Rx.Observable.create((subscriber) => { for (const item of iterable) { // This need be blocking subscriber.next(item); } subscriber.complete(); }); // This cause blocking… iterableToObservable.subscribe((v) => console.log(v));

Slide 30

Slide 30 text

e.g. Observable -> Iterable (Sync) class ObservableToIter { constructor(source) { this._cache = cache; source.subscribe((v) => this._cache.push(v)); } next() { const done = this._cache.length > 0; let value = undefined; if (done) { value = this._cache.shift(); } return { done, value, }; } [Symbol.iterator]() { return this; } } const iter = new ObservableToIter(source); iter.next();  Limitations (Problems)  On consuming first value or initializing the converter, we need to get all value from sequence.  If observable returns value async, we don't transform observable to iterable, or wait to complete observable with blocking!  If observable is infinite sequence, we cannot return to consumer.

Slide 31

Slide 31 text

https://twitter.com/headinthebox/status/669963949027160064

Slide 32

Slide 32 text

e.g. Async Iteration (proposal for ECMA262) const result: Promise> = asyncIter.next(); result.then((done, value) => console.log(done, value)); for await (let value of asyncIter) { // not blocking // blah, blah, blah }

Slide 33

Slide 33 text

e.g. AsyncIterable -> Observable (PoC) // This can push all values async const asyncIterableToObservable = Rx.Observable.create((subscriber) => { for await (const item of asyncIterable) { // This need not to be blocking subscriber.next(item); } subscriber.complete(); }); asyncIterableToObservable.subscribe((v) => console.log(v));

Slide 34

Slide 34 text

e.g. Observable -> AsyncIterable (PoC) class ObservableToIter { constructor(source) { this._buffer = []; this._resolverCache = []; this._done = false; this._current = 0; source.subscribe((v) => { this._buffer.push({ ok: true, value: v, }); const lastIndex = this._buffer.length - 1; const resolver = this._resolverCache[lastIndex]; if (resolver !== undefined) { resolver({ done: this._done, value: v, }); this._resolverCache[lastIndex] = undefined; } }, () => {}, () => { this._done = true; for (const resolver of this._resolverCache) { if (resolver === undefined) { continue; } resolver({ done: true, }); } this._resolverCache = []; }); } next() { const done = this._done; if (done) { return Promise.resolve({ done }); } const current = this._current; this._current++; const result = this._buffer[current]; if (result !== undefined && result.ok) { return Promise.resolve({ done, value: result.value, }); } else { return new Promise((resolve) => { this._resolverCache[current] = resolve; }); } } [System.asyncIterator]() { return this; } } const source = Rx.Observable.create((o) => { const buffer: any = []; for (const i of [1, 2, 3, 4, 5]) { buffer.push(new Promise((r) => { window.setTimeout(() => { r(); o.next(i); }, i * 1000); })); } Promise.all(buffer).then(() => o.complete()); }); const iter = new ObservableToIter(source); await iter.next() // done: false, value: 1 await iter.next(); // done: false, value: 2 await iter.next(); // done: false, value: 3 await iter.next(); // done: false, value: 4 await iter.next(); // done: false, value: 5 await iter.next(); // done: false, value: undefined

Slide 35

Slide 35 text

Summary  Events are collections, we can query them  Push/Pull is duality of APIs Which is more suitable for your case?  Rx is advanced observer pattern implementation that provides highly composable push APIs