rxjs in-depth - angularconnect 2015

79
RxJS In-Depth (Formerly a talk called “Reactive Streams in Angular 1 and 2”)

Upload: ben-lesh

Post on 13-Jan-2017

2.237 views

Category:

Technology


3 download

TRANSCRIPT

RxJS and Angular

RxJS In-Depth(Formerly a talk called Reactive Streams in Angular 1 and 2)

We had a last-minute pivot on talk content, because Jeff Cross and Rob Wormald wanted to ruin my week last week.

Ben LeshSenior UI EngineerEdge Developer Experience

RxJS Nexthttp://github.com/ReactiveX/RxJS

RxJS Next is now RxJS 5.0.0-alpha

RxJS 5 is community drivenSpear-headed by contributors at Netflix, Google and Microsoft. With many other contributors under the ReactiveX community banner.

RxJS 5 GoalsPerformanceImproved debuggingES7 observable spec alignment

PerformanceDecisions were guided by performance testingTried out 4-5 different architectures in the beginning> 120 micro performance tests~ 10 macro performance tests around common scenarios and bottlenecks

RxJS 5 operators are4.3x faster on average

Call-stack depth and debugging was a common complaint in older versions of RxJS

RxJS 4

RxJS 5

Thanks in huge part to contributions from

Paul TaylorOJ KwonAndr StaltzJeff Cross

Luis GabrielAdrian OmeloJinrohRob Wormwald

really every one of our contributors.https://github.com/ReactiveX/RxJS/graphs/contributors

Thanks to guidance from

Matt PodwysockiJafar HusainErik MeijerBen ChristensenGeorge CampbellAaron Tull

were closing in on beta in the next month or so

What is RxJS?Reactive programmingTreating events as collectionsManipulating sets of events with operators

RxJS is LoDash or Underscore for async.

Angular 2 is usingRxJS 5 Observables for async

Angular has traditionally used Promises for async

PromisesRead-only view to a single future valueSuccess and error semantics via .then()Not lazy. By the time you have a promise, its on its way to being resolved.Immutable and uncancellable. Your promise will resolve or reject, and only once.

ObservableStreams or setsOf any number of thingsOver any amount of timeLazy. Observables will not generate values via an underlying producer until theyre subscribed to.Can be unsubscribed from. This means the underlying producer can be told to stop and even tear down.

Both Promises and Observablesare built to solve problems around async(to avoid callback hell)

Types of async in modern web applicationsDOM events AnimationsAJAXWebSocketsSSEAlternative inputs (voice, joystick, etc)

Promises only really make sense for one of theseDOM events (0-N values)Animations (cancelable)AJAX (1 value)WebSockets (0-N values)SSE (0-N values)Alternative inputs (0-N values)

except when they dontSingle page applications commonly prepare data via AJAX for each view shownWhen the view changes, the next view probably doesnt care about the previous views dataFortunately, XHRs can be aborted!but promise-based AJAX implementations cannot be aborted, because promises cannot be cancelled.*

So how do we go from Promises to Observables?

They have some similar semanticsthen x.(valueFn, errorFn);subscribe

They have some similar semanticsx.subscribe(valueFn, errorFn);, completeFn

Observable also has cancellation semantics!let sub = x.subscribe(valueFn, errorFn, completeFn);

// ...some time latersub.unsubscribe();

Creating Observables is similar to creating Promises

creating a Promiselet p = new Promise((resolve, reject) => { doAsyncThing((err, value) => { if (err) { reject(err); } else { resolve(value); } });});

creating an Observablelet o = new Observable(observer => { doAsyncThing((err, value) => { if (err) { observer.error(err); } else { observer.next(value); observer.complete(); } });});

if the producers resource allows cancellation, we can handle that herelet o = new Observable(observer => { const token = doAsyncThing((err, value) => { if (err) { observer.error(err); } else { observer.next(value); observer.complete(); } });

});const token return () => { cancelAsyncThing(token); };

when you call o.subscribe()let o = new Observable(observer => { const token = doAsyncThing((err, value) => { if (err) { observer.error(err); } else { observer.next(value); observer.complete(); } });

return () => { cancelAsyncThing(token); };}); observer => { const token = doAsyncThing((err, value) => { if (err) { observer.error(err); } else { observer.next(value); observer.complete(); } });

return () => {

};});

when you call subscription.unsubscribe()let o = new Observable(observer => { const token = doAsyncThing((err, value) => { if (err) { observer.error(err); } else { observer.next(value); observer.complete(); } });

return () => { cancelAsyncThing(token); };});

return () => { cancelAsyncThing(token); };

Dont worry... youre unlikely to be creating your own Observables

Observable creation helpers in RxJSObservable.of(value, value2, value3, )Observable.from(promise/iterable/observable)Observable.fromEvent(item, eventName)Angulars HTTP and Realtime Data servicesMany community-driven RxJS modules and libraries

Error Handling in Observables is also similar to Promise

catchmyPromise.catch(error => { if (error instanceof OkayError) { return Promise.resolve(okay); } else { throw error; }});

catchmyObservable.catch(error => { if (error instanceof OkayError) { return Observable.of(okay); } else { throw error; }});

finally(some promise impls)myPromise.finally(() => { console.log(done);});

finallymyObservable.finally(() => { console.log(done);});

Observables can be retriedmyObservable.retry(3);

myObservable.retryWhen(errors => errors.delay(3000));

Quick Recap on ObservableAn observable is a set of any number of things over any amount of time.

Quick Recap on ObservableAll values are pushed to the nextHandler

observable.subscribe(nextHandler);

Quick Recap on ObservableThe errorHandler is called if the producer experiences an error

observable.subscribe(nextHandler, errorHandler);

Quick Recap on ObservableThe completionHandler is called when the producer completes successfully

observable.subscribe(nextHandler, errorHandler, completionHandler);

Quick Recap on ObservableObservables are lazy. It doesnt start producing data until subscribe() is called.

observable.subscribe(nextHandler, errorHandler, completionHandler);

Quick Recap on Observablesubscribe() returns a subscription, on which a consumer can call unsubscribe() to cancel the subscription and tear down the producer

let sub = observable.subscribe(nextHandler, errorHandler, completionHandler);sub.unsubscribe();

What are operators?Theyre methods on Observable that allow you to compose new observables.

How operators worklet result = source.myOperator();

result is an Observable, that when subscribed to, subscribes to source, then tranforms its values in some way and emits them.

How operators worksubscription.unsubscribe();

when unsubscribe() is called on that subscription, the unsubscription logic from both result and source are called.

How operators workOperators pass each value from one operator to the next, before proceeding to the next value in the set.*This is different from array operators (think map and filter) which will process the entire array at each step.*This can be changed with Schedulers.

Array filter, map, reduce

Observable filter, map, reduce

simple operatorsmap()filter()reduce()first()last()single()elementAt()toArray()isEmpty()take()skip()startWith()and many more

merging and joining operatorsmergemergeMap (flatMap)concatconcatMapswitchswitchMapcombineLatestwithLatestFromzipforkJoinexpand

splitting and grouping operatorsgroupBywindowpartition

buffering strategy operatorsbufferthrottledebouncesample

With ES7 function bind, you can roll your own*(*coming soon to TypeScript?)

hot vs cold observablesObservables are cold by defaultCold observables create a new producer each time a consumer subscribes to themHot observables share a single producer with every consumer that subscribes to them.

Multiplexed WebSocketConnect to a web socket serverFor each data streamsend a subscription message to the serverfilter out responses from the serverwhen done, send an unsubscription message.Ideally, the socket is only open while you require data from it.

Multiplexed WebSocketRECONNECTION?!maintain a state of all of the multiplexed data streams your views need.check to see when youre able to reconnectreconnect the socketonce the socket is reopened, send all of the subscription messages again

Create an Observable that wraps a WebSocket

It will create the WebSocketwhen you subscribe()

It emits messages eventsthat arrive on the WebSocket

And when it unsubscribes,it will tear down the WebSocket!

Share to make it hotWe dont want each subscriptioncreating its own websocket.

Create an Observable that wraps ourSocket Observable

filter the messagesfrom the Socket Observableto what you needand subscribe

Send a message tothe server to start gettingthe data over the socket

on unsubscribe, tell the server to stop sending dataand unsubscribe from the socket observable

Add a retryWhen to retry the whole thingif something goes wrong with the multiplexed observable

share the observable to make it hot, so many consumers can share this producer.

Example Time!

Never trust anyone with only good things to say about their library/framework.

Whats bad in RxJS?Zalgo.. dont use mutable outer scoped variables in your operators or subscriptions!Unbounded buffers! The zip operator in particular is dangerous here.Too many operators to remember.

Twitter: @benleshgithub: [email protected]: Comments.BETTER: Questions.BEST: Corrections!Thank you!