angular2 rxjs

Post on 13-Jan-2017

401 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Rxjs for angular 2christoffer noring

google developer expert

In the beginningThere was callback hell

Unreadable

Angular 1 and promises

We got more ordered code with constructs like

get() .then( doSomething ) .then( somethingElse ) .then( lastAction ) .catch( catchAll )

No callback hell anymore :)

We could do stuff like first do A then based on As result do B

$q.when( data )

function doSomethingAsync(){

var deferred = $q.defer(); setTimeout( () => { if(someCondition) { deferred.resolve( data ) } else { deferred.reject( err ); } },3000 ) return deferred.promise;

}

OR

We could wrap up async code in a nice way

doSomethingAsync().then((data) => { console.log(data);}, (err) => { console.log(err);})

Use this one1

2

function doAsync(){new Promise( (resolve, reject) =>

{ setTimeout({ resolve(data); },3000);})

}

doAsync().then((data) => {}, err => {

})

Or we could use native promises

Promise.all( get(), getMore().. )

Everything was great or?

Issues with promisesNot cancellable

We need to write a lot of code to retry our logic, becomes messy

Only returns one value

Not easy to compose with other things like callbacks and event etc, although them being async in their nature

Cumbersome to retry

A better way, observables

stream of values over time

1 2 3 4 5

time6

Enter observablesvar stream = new Rx.Observable.create( fnSuccess,fnError,fnCompleted )

stream.subscribe((data) => { console.log( “Data”,data );})

nothing happens till someone subscribes

Observable with error

var stream = Rx.Observable.create(function(observer){ observer.onNext(1); observer.onNext(2); observer.onNext(3); observer.onError( ‘there is an error’ )})

stream.subscribe( function(data){ console.log( data ); }, function(error) { console.error( error ); })

var stream = Rx.Observable.create(function(observer){ observer.onNext( 1 ); observer.onNext( 2 ); observer.onNext( 3 ); observer.onCompleted();})

stream.subscribe( function(data){ console.log( data ); }, function(error){ console.error( error ); }, function(){ console.info( “completed” ); } )

Observable with completion

var homemadeStream = Rx.Observable.create((observer) => { var i=0;

var handle = setInterval(function() { observer.onNext( i++ ); }, 500);

return function(){ console.log('Disposing timeout'); clearTimeout( handle ); }});

Observable with cancellation

var subscription2 = homemadeStream.subscribe((val) => { console.log('Homemade val',val);});

setTimeout(function() { console.log('Cancelling homemadeStream'); subscription2.dispose();}, 1500);

we clean upno more values are emitted

.unsubscribe()

Wrapping something into an observer

var stream = Rx.Observable.create(function(observer){ var request = new XMLHttpRequest();

request.open( ‘GET’, ‘url’ ); request.onload = function(){ if(request.status === 200) { observer.onNext( request.response ); observer.onCompleted(); } else { observer.onError( new Error( request.statusText ) ) } } request.onerror = function(){ observer.onError( new Error(‘unknown error’) ); } request.send();})

1

2

3

3

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

} err => {}() => {})

132

Everything is a stream

You an combine async concepts

click eventsuser inputsdata from server

callbackssocket

You can create observable by hand but is more likely to create them from

something

Different ways to create an observable

Rx.Observable.fromArray([ 1,2,3,4 ])

Rx.Observable.fromEvent(element, ‘event’);Rx.Observable.fromArray(eventEmitter, ‘data’, function(){})

Rx.Observable.fromNodeCallback(fs.createFile)

Rx.Observable.fromCallback(obj.callback)

Rx.Observable.range(1,3)Rx.Observable.interval(miliseconds)

Operators gives observable their power

var stream = Rx.Observable.interval(500).take(5).filter((val) = > { return val % 2 === 0;}).map((val) => { return “val” + 1;})

Operators

stream.subscribe(() => {})

operator is a function that returns an observable

emit value every 500 ms

limit number of values

operator: change the value

operator: only emit certain values

Wait, it looks like loadash?

Comparisonlist.map( x = > x.prop ).filter( x => x > 2 ).take( 2 )

list.map( x = > x.prop ).filter( x => x > 2 ).take( 2 ).subscribe( x => console.log(x), err => console.log(err)

)service.get().then( x => console.log(x) ).catch( err => console.log(err) )

Array like, handles async

but can also- Cancelled- Retried

lodash/array functions

async

observable

Operatorsoverview

120+ operators ( 60+ Rxjs5 )

Combination Conditional

Creational

Error handling

Filtering

Transformation

Utility

Categories

How am I gonna keep track of all that?

Use the most common ones, and then expand your knowledge

Look at marble diagrams which one you need

Also create your own to help you in the coding process

Stream

Other stream

Resulting stream

1

Most operators are covered at rxmarbles.com

2 3

4 5

1 2 3 4 5

Marble diagramsstream/s + operation = new stream

operation by the operator

Start with these operators

map

filter

flatMap

switchMap

do

merge

of

interval

take

ofRx.Observable.of(1,2,3,4).subscribe((val) => {console.log( val )

})

Emits exactly whats in there

Good for prototyping an idea

mapRx.Observable.of(1,2,3).map((val) => { return “” + val;})

Point is to take each value, change it and return a new value in its place

filterRx.Observable.of(1,2,3).filter((val) => {return val % 2 ===

0;}).

Point is to reduce the number of values

flatMapRx.Observable.of(1,2,3).map( (val) => {

} ) return Rx.DOM.getJSON( ‘data’ + val +‘.json' )

Becomes a list of observables, hard to work with

Rx.Observable.of(1,2,3).flatMap( (val) => {

} )return Rx.DOM.getJSON( ‘data’ + val +‘.json' )

Flatten all observables to a meta stream

flatMap - example 2, auto complete

flatmapExample = Rx.Observable.fromEvent(input,'keyup').map( function(ev){ return ev.target.value;}).filter(function(text){ return text.length >=3;}).distinctUntilChanged().flatMap( function(val){ return Rx.DOM.getJSON( 'data3.json' );})

flatmapExample.subscribe( function(result){ console.log('Flatmap', result);})

Transform event to char

Wait until we have 3 chars

Only perform search if this ‘search’ is unique

flatMapIs for scenarios when you come from one type of streams and wants it to become something completely else like a stream of clicks becomes a stream of ajax calls

clicks ajax persons

Also avoid thisajax

clicks

ajax

ajax

Subscribe

Subscribe

Subscribe

Instead becomeajax

clicks

ajax

ajax

ajax personsflat mapmap

one subscribe

switchMap

Switch map, complete something based on a condition

breakCondition = Rx.Observable.fromEvent(document,'click');breakCondition.switchMap( function(val){ return Rx.Observable.interval(3000).mapTo('Do this if nothing breaks me');})

breakCondition.subscribe( function(val){ console.log('Switch map', val);})

dovar stream = Rx.Observable.of(1,2,3,4,5);

var subscription = stream.do(function(val){ console.log('Current val', val);}).filter(function(val){ return val % 2 === 0;});

subscription.subscribe(function(val){ console.log('Val',val);})

Echos every value without changing it,

used for logging

concat/merge

concat

merge

var concat = Rx.Observable.concat( stream, stream2 );

var stream = Rx.Observable.fromArray([1,2,3,4]);

var stream2 = Rx.Observable.fromArray([5,6,7,8]);

1, 2, 3, 4, 5, 6, 7 ,8

var merge = Rx.Observable.merge( stream, stream2 );

1, 5, 2, 6, 3, 7, 4 ,8

first stream emits all values then remaining value

Needs queue Scheduler in Rxjs5

Other useful operators

Delay,the whole sequence

stream.delay(1000).subscribe( (val) => { var newTime = new Date().getTime(); console.log('Delayed', val + " " + (newTime - time));})

Rx.Observable.merge( Rx.Observable.of('Marco').delay(1000), Rx.Observable.of('Polo').delay(2000)).subscribe((val) => { var newTime = new Date().getTime(); console.log('Merged Delay', val + " " + (newTime - time));})

Delay….. 1 second

123

….. 1 second Marco

….. 2 secondPolo

Debouncevar debounceTime = Rx.Observable.fromEvent(button,'click').debounce(2000);

debounceTime.subscribe( function(){ console.log('mouse pressed');})

Ignores all generated mouse click events

for 2 seconds

click click click click click

ignore ignore ignore ignore use

…. 2 seconds passed

Fetching data with fetch api and promise

fetch(‘data.json',init).then(function(response) { return response.blob();})

var init = { method: 'GET', headers: myHeaders, mode: 'cors', cache: 'default' };

There is always this one if you don’t want to use Observables

Angular 2 and rxjs

router, most actions are observables

http lib is observables

What about angular 2 and rxjs,how is it used?

Fetching datawith Observable

getAll() { return http.get(`${baseUrl}`/people, {}) .map( mapPeople )}

getAll().subscribe((people) => { console.log(‘people’, people);}).catch((err) => { console.log(‘Error’, err);})

mapPeople(response:Response) { return response.json().results.map(toPerson)}

fetch data, map result, handle error

Fetch data Observable

cancellingvar stream = getAll() { return http.get(`${baseUrl}`/people, {}) .map( mapPeople )}

setTimeout(() => { stream.dispose();},3000);

stream.subscribe((data) => {})

unsubscribe() in rxjs5

BUT we can cancel!!

Fetch data Observable

retry

var stream = Rx.DOM.get('/products.json').retry(5);

stream.subscribe((val) => { console.log('Data', val);}, err => console.log(err));

5 failed attempts then we hit error callbackIts so easy to retry, imagine how messy this code would be with a promise

SubjectObserver Observable

Subject

Inherits from both

the subject can act as a proxy for a group of subscribers and a source

var subject = new Rx.Subject();

subject.subscribe((val) => { console.log( 'Produced by subject', val );});

subject.onNext(1);subject.onCompleted();

Acts like an observer

Acts like an observableSubject

var subject = new Rx.Subject();

var source = Rx.Observable.interval(500);

source.subscribe(subject);

subject.subscribe( (val) => { console.log('Sub', val); }, (err) => console.log(err), () => console.log('completed'));

setTimeout(function() { subject.onCompleted();}, 3000);

Pass subject as an observer

Receives all the values pushed out by the source

Able to stop receiving values

Subject

var subject = new Rx.Subject();

var source = Rx.Observable.interval(500).take(3);

source.subscribe( subject );

subject.subscribe((val) => { console.log('Subject', val);});

subject.onNext('Mess1');subject.onNext('Mess2');

setTimeout(function() { subject.onCompleted();}, 1600);

Listens to all values from source

Add to stream

Order important

SubjectonNext() before subscribe is lost

var subject = Rx.ReplaySubject();subject.onNext(1);

subject.subscribe((val) = > { console.log('Replay', val);})

subject.onNext(2);subject.onNext(3);

var subject = Rx.Subject();subject.onNext(1);

subject.subscribe((val) = > { console.log('Replay', val);})

subject.onNext(2);subject.onNext(3);

Normal subject, everything before subscribe is lost

Replay subject, nothing is lost

Subject - types

BehaviourSubject

Good for default values

/* Initialize with initial value of 42 */var subject = new Rx.BehaviorSubject(42);

var subscription = subject.subscribe( function (x) { console.log('Next: ' + x.toString()); }, function (err) { console.log('Error: ' + err); }, function () { console.log('Completed'); });

subject.onNext(56);

subject.onCompleted();// => Completed

Init/Default value

Next: 42

Next: 56

Angular 2 with rxjsRxjs is a big library, if you just have request/reponse then do

import 'rxjs/add/operator/map';One import per used operator

import * as Rx from 'rxjs/Rx';imports the whole of Rxjs !!!, GOOD for development BAD for production Then carry on coding

1

1a

Component to ComponentYou want something that can produce value,

that you can listen to

this.subject = new Rx.Subject();

function sendData(data){ this.subject.next( data )}

function getSubject(){ return this.subject;}

//service implComponent

1

Component1

service.getSubject().subscribe(()=>{})

service.sendData( data )

BUT, can be made nicer with a uniform data flow

Angular 2 rxjs setup

ngOnInit(){ this.subscription = this.service.getData();}

ngOnDestroy(){ this.subscription.unsubscribe();}

Setup

Teardown

life cycle hooks

Angular 2So when to use it?

Its a better promiseCancelling

Retry

Want to combine callbacks, event and data fetch?Less code with rxjs

there is a forkJoin operator that replace Promise.all() etc

anything you can do, I can

do betterno you can’t :(

Observable Promise

Testing

Solution : bending time with schedulers

Hard to test right?

Some methods might take minutes?

Schedulersbending time

Because scheduler has its own virtual clockAnything scheduled on that scheduler will adhere to time denoted on the clock

I.e we can bend time for ex unit testing

var onNext = Rx.ReactiveTest.onNext;var scheduler = new Rx.TestScheduler();var subject = scheduler.createColdObservable( onNext(100,'first'), onNext(200,'second'));

var result;

subject.subscribe((val) => { result = val;});

scheduler.advanceBy( 100 );

console.log('Should equal', result === 'first');

scheduler.advanceBy( 100 );

console.log('Should equal', result === 'second');

Advance timeAssert

Advance timeAssert

create observable from scheduler

emit values

var testScheduler = new Rx.TestScheduler();

var stream = Rx.Observable.interval(1000, testScheduler).take(5).map((val) => { return val + 1}).filter((i) => { return i % 2 === 0});

var result;stream.subscribe((val) => result = val );

console.log('testing function’);

testScheduler.advanceBy(1000);testScheduler.advanceBy(1000);testScheduler.advanceBy(1000);

console.log('Should equal', result === 2);

testScheduler.advanceBy(1000);testScheduler.advanceBy(1000);console.log('Should equal', result === 4);

replace default scheduler

0 1 2

Error scenarios

Capture error in .subscribe()

Error handlingcatchvar errStream = Rx.Observable.throw('Error');

var stream = Rx.Observable.create(function(observer){ observer.onNext(1); })

var merged = Rx.Observable.merge( errStream, stream )

merged.subscribe( function(val){ console.log('Val', val);}, function(err){ console.log('Err', err);}, function(){ console.log('completed');})

Captured here but sequence interrupted,completed NOT reached

Capture error in .subscribe() + completed stream

Error handling

Error handlingimproved catch

var errStream = Rx.Observable.throw('Error');

var stream = Rx.Observable.create(function(observer){ observer.onNext(1); })

var merged = Rx.Observable.merge( errStream, stream ).catch(function(err){ return Rx.Observable.of(err);});

merged.subscribe( function(val){ console.log('Val', val);}, function(err){ console.log('Err', err);}, function(){ console.log('completed');})

Captured here but sequence interrupted,completed IS reached

stream not processed thoughcan we improve it?

stream killed by errStream:(

Wrap error stream before merging with other streams so we don’t kill other valid streams

Error handling

Error handlingignoring

We need to handle the erroring stream betterFrom

var errStream = Rx.Observable.throw('AAA thump..')

Tovar errStream = Rx.Observable.throw('AAA thump..').catch(function(err){ return Rx.Observable.of(err);});

This will emit all values and the wrapped error

Process all values and errors by wrapping the errors,everything gets processed

Error handling

Error - ignoringother scenario

var merged = Rx.Observable.merge( errStream2, stream )

merged.subscribe( function(val){ console.log('Val', val);}, function(err){ console.log('Err', err);}, function(){ console.log('completed');})

1,1,2

var errStream2 = Rx.Observable.interval(200).take(3).select(function(val){ if(val === 0) { return Rx.Observable.throw('Error stream'); } else { return Rx.Observable.of(val); }}).select(function(observable){ return observable.catch(Rx.Observable.return('Error handled'));}).selectMany( function(x){ return x;});

wrap

catch and rewrite

Set a policy for error handling of streams when merging so successful stream survive

Error handling

Error - ignoringother scenario

You have several sources and two terminatesYou want the other normal source to work

var errStream = Rx.Observable.throw('AAA thump..');

var errStreamWithFlattenedData = Rx.Observable.interval(500).take(3).flatMap( function(val){ if( val === 1 ) { return Rx.Observable.throw('crashing'); } else { return Rx.Observable.return(val); }})

var normalStream = Rx.Observable.return('anything');

var handledErrorStream = Rx.Observable.onErrorResumeNext( errStream, normalStream, errStreamWithFlattenedData );handledErrorStream.subscribe(function(err){ console.log('error stream ignored', err);},function(err){ console.log("error stream ignored, error",err);}, function(){ console.log("completion of error stream ignored");})

Ensure good streams survive in a merge of failing streams, onErrorResumeNext

Further reading

https://xgrommx.github.io/rx-book

http://www.learnrxjs.io/

bacon.js

Thank you

top related