rxjs evolved

Post on 13-Jan-2017

1.945 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

RXJS EVOLVED

PAUL TAYLOR@trxcllnt

UI PLATFORM TEAM @

OBSERVABLE

EVENTEMITTER..... OBSERVABLE ≠ EVENTDISPATCHER

EVENTDELEGATE...

“The idea of future values — maybe.”

–ME

SINGLE MULTIPLE

SYNCHRONOUS Function Enumerable

ASYNCHRONOUS Promise Observable

THE OTHER “IDEAS” OF VALUES

SINGLE MULTIPLE

PULL Function Enumerable

PUSH Promise Observable

THE OTHER “IDEAS” OF VALUES

FUNCTIONS// The “idea” of a random number

var getRandomNumber = function() {

return Math.random();

}

// A random number

var rand = getRandomNumber.call();

(LAZINESS)

ANATOMY OF OBSERVABLECREATION

SUBSCRIPTION DISPOSAL

var randomNumbers = Observable.create((s) => {

var i = setTimeout(() => {

s.next(Math.random());

s.complete();

}, 1000);

return () => clearTimeout(i);

});

var sub = randomNumbers.subscribe({

next(x) { console.log(x); },

error(e) { console.error(e); },

complete() { console.log(“done”); }

});

var randomNumbers = Observable.create((s) => {

var i = setTimeout(() => {

s.next(Math.random());

s.complete();

}, 1000);

return () => clearTimeout(i);

});

var randomNumbers = Observable.create((s) => {

var i = setTimeout(() => {

s.next(Math.random());

s.complete();

}, 1000);

ANATOMY OF OBSERVABLE★ CREATION

★ SUBSCRIPTION

★ DISPOSAL

var randomNumbers = Observable.create(

WHAT HAPPENS WHEN…var randomNumbers = Observable.create((s) => {

var i = setTimeout(() => {

s.next(Math.random());

s.complete();

}, 1000);

return () => clearTimeout(i);

});

randomNumbers.subscribe(x => console.log(‘1: ’ + x));

randomNumbers.subscribe(x => console.log(‘2: ’ + x));

>

> 1: 0.1231982301923192831231

> 2: 0.8178491823912837129834

>

THIS HAPPENS

EVENTEMITTER..... OBSERVABLE ≠ EVENTDISPATCHER

EVENTDELEGATE...

OBSERVABLE = FUNCTION...........

“Observable is a function that, when invoked, returns 0-∞ values between now and the end of time.”

–ME

OPERATORS

OPERATORSMETHODS THAT PERFORM

CALCULATIONS ON THE VALUES

MAP, FILTER, SCAN, REDUCE, FLATMAP, ZIP, COMBINELATEST, TAKE, SKIP, TIMEINTERVAL, DELAY, DEBOUNCE, SAMPLE, THROTTLE, ETC.

“lodash for events”–NOT ME

WRITING AN OPERATOR (OLD)class Observable {

constructor(subscribe) { this.subscribe = subscribe; }

map(selector) {

var source = this;

return new Observable((destination) => {

return source.subscribe({

next(x) { destination.next(selector(x)) },

error(e) { destination.error(e); },

complete() { destination.complete(); }

});

});

}

}

USING OPERATORSvar grades = { “a”: 100, “b+”: 89, “c-“: 70 };

Observable.of(“a”, “b+”, “c-”)

.map((grade) => grades[grade])

.filter((score) => score > grades[“b+”])

.count()

.subscribe((scoreCount) => {

console.log(scoreCount + “ students received A’s”);

});

SCHEDULERS

SCHEDULERSCENTRALIZED DISPATCHERS TO

CONTROL CONCURRENCY

IMMEDIATE TIMEOUT

REQUESTANIMATIONFRAME

USING SCHEDULERSObservable.range = function(start, length, scheduler) {

return new Observable((subscriber) => {

return scheduler.schedule(({ index, count }) => {

if (subscriber.isUnsubscribed) { return; }

else if (index >= end) {

subscriber.complete();

} else {

subscriber.next(count);

this.schedule({ index: index + 1, count: count + 1 });

}

}, { index: 0, count: start });

});

}

USING SCHEDULERSObservable.fromEvent(“mouseMove”, document.body)

.throttle(1, Scheduler.requestAnimationFrame)

.map(({ pageX, pageY }) => (<div

className=“red-circle”

style={{ top: pageX, left: pageY }} />

))

.subscribe((mouseDiv) => {

React.render(mouseDiv, “#app-container”);

});

RXJS NEXT (v5.0.0-alpha)github.com/ReactiveX/RxJS

CONTRIBUTORS

BEN LESH ANDRÈ STALTZ OJ KWON

github.com/ReactiveX/RxJS/graphs/contributors

PRIMARY GOALS★ MODULARITY

★ PERFORMANCE

★ DEBUGGING

★ EXTENSIBILITY

★ SIMPLER UNIT TESTS

import Observable from ‘@reactivex/rxjs/Observable’;

import ArrayObservable from

‘@reactivex/rxjs/observables/ArrayObservable’;

import reduce from ‘@reactivex/rxjs/operators/reduce’;

Observable.of = ArrayObservable.of;

Observable.prototype.reduce = reduce;

Observable.of(1, 2, 3, 4, 5)

.reduce((acc, i) => acc * i, 1)

.subscribe((result) => console.log(result));

MODULARITY

SPEED

4.3X* FASTER*AVERAGE

(UP TO 11X)

50-90% FEWER

ALLOCATIONS

MEMORY

10-90% SHORTER

CALL STACKS

DEBUGGING

DEBUGGINGFLATMAP EXAMPLE RXJS 4

DEBUGGINGFLATMAP EXAMPLE RXJS 5

DEBUGGINGSWITCHMAP EXAMPLE RXJS 4

DEBUGGINGSWITCHMAP EXAMPLE RXJS 5

PERFORMANCE + DEBUGGING

★ SCHEDULERS OVERHAUL

★ CLASS-BASED OPERATORS (“LIFT”)

★ UNIFIED OBSERVER + SUBSCRIPTION

★ FLATTENED DISPOSABLE TREE

★ REMOVE TRY-CATCH FROM INTERNALS

FLATMAP VS. LIFT

flatMap<T, R>(selector: (value: T) => Observable<R>): Observable<R>;

lift<T, R>(operator: (subscriber: Observer<T>) => Observer<R>): Observable<R>;

FLATMAP

Observable.prototype.map = function map(project) {

var source = this;

return new Observable(function (observer) {

return source.subscribe(

(x) => observer.next(project(x)),

(e) => observer.error(e),

( ) => observer.complete()

);

});

}

★ CLOSURE SCOPE

★ INFLEXIBLE TYPE

★ CLOSURES SHOULD BE ON A PROTOTYPE

Observable.prototype.map = function(project) => { return this.lift(new MapOperator(project));}

class MapOperator implements Operator { constructor(project) { this.project = project; } call(observer) { return new MapSubscriber(observer, this.project); }}

class MapSubscriber extends Subscriber { constructor(destination, project) { super(destination); this.project = project; } next(x) { this.destination.next(this.project(x)); }}

★ NO CLOSURES

★ DELEGATES NEW OBSERVABLE TO LIFT

★ USES SUBSCRIBER PROTOTYPE

LIFT

EXTENSIBILITY

★ ALLOW SUBCLASSING OBSERVABLE

★ MAINTAIN SUBJECT BI-DIRECTIONALITY

★ FUTURE BACK-PRESSURE SUPPORT?

SUBCLASS OBSERVABLEclass MouseObservable extends Observable {

constructor(source, operator) {

this.source = source;

this.operator = operator || ((x) => x);

}

lift(operator) { return new MouseObservable(this, operator); }

trackVelocity() {

return this.lift((destination) => {

return new VelocityScanningSubscriber(destination);

});

}

concatFrictionEvents(coefficientOfKineticFriction) { ... }

}

SUBCLASS OBSERVABLEObservable.fromEvent(“mousedown”, document.body)

.flatMap((downEvent) => {

return Observable.fromEvent(“mousemove”, window)

.let(_ => new MouseObservable(this))

.trackVelocity()

.takeUntil(Observable.fromEvent(“mouseup”, window))

.concatFrictionEvents(0.5)

})

.throttle(1, Schedulers.requestAnimationFrame)

.subscribe(({ x, y }) => {

item.style.top = y;

item.style.left = x;

});

MAINTAIN TWO-WAY SUBJECTSvar naviSocket = new SocketSubject(“ws://127.0.0.1/navi”)

.map(x => JSON.parse(x.data))

.throttle(100);

naviSocket.subscribe((msg) => {

if (msg == “hey, listen!”) {

naviSocket.next(“go away navi!”);

}

});

SIMPLER UNIT TESTSMARBLE DIAGRAMS AS UNIT TESTS

SIMPLER UNIT TESTSMARBLE DIAGRAMS AS UNIT TESTS

it('should filter out even values', function() {

var source = hot('--0--1--2--3--4--|');

var expected = '-----1-----3-----|';

expectObservable(source.filter(x => x % 2 == 1)).toBe(expected);

});

RXJS NEXT (v5.0.0-alpha)github.com/ReactiveX/RxJS

top related