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 OBSERVABLE
CREATION
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
OPERATORS
METHODS 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 OPERATORS
var 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
SCHEDULERS
CENTRALIZED DISPATCHERS TO
CONTROL CONCURRENCY
IMMEDIATE

TIMEOUT

REQUESTANIMATIONFRAME
USING SCHEDULERS
Observable.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 SCHEDULERS
Observable.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
DEBUGGING
FLATMAP EXAMPLE RXJS 4
DEBUGGING
FLATMAP EXAMPLE RXJS 5
DEBUGGING
SWITCHMAP EXAMPLE RXJS 4
DEBUGGING
SWITCHMAP 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 OBSERVABLE
class 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 OBSERVABLE
Observable.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 SUBJECTS
var 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 TESTS
MARBLE DIAGRAMS AS UNIT TESTS
SIMPLER UNIT TESTS
MARBLE 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

RxJS Evolved