rxjs ngvikings
TRANSCRIPT
![Page 1: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/1.jpg)
Rxjs everything is a stream
Christoffer NoringGoogle Developer Expert
@chris_noring
![Page 2: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/2.jpg)
Why Rxjs?We want to deal with async in a “synchronous looking way”
We want something better than promises
We want one paradigm for async to rule them all
![Page 3: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/3.jpg)
Once upon a time in async land…There were callbacks
Callbacks turned into callback hell
![Page 4: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/4.jpg)
Promises to the rescue
service.getData()
.then(getMoreData) .then(getEvenMore) .then(andSomeMore)
Looks great right?
![Page 5: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/5.jpg)
But promises were flawedNo cancellation
Can’t deal with other async concepts like mouse positions, clicks, user input
No rich composition
And brexit happened
Cumbersome to retryOnly returns one value
![Page 6: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/6.jpg)
Help us ObservablesYou’re my only hope
![Page 7: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/7.jpg)
What is an observableObservable is just a function
that takes an observer and returns a functionObserver: an object with next, error, complete methods
Rx.Observable.create((observer) => { observer.next(1); observer.error(‘error’); observer.complete();})
1 2 3 4 5 6 7
stream of value over time
![Page 8: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/8.jpg)
Promise vs Array
vs Observable
list.map( x = > x.prop ).filter( x => x > 2 ).take( 2 )
Arraylist.map( x = > x.prop ).filter( x => x > 2 ).take( 2 ).subscribe(
x => console.log(x), err => console.log(err)
)
Observable
Promiseservice.get().then( x => console.log(x) ).catch( err => console.log(err) ) but can also
- Cancelled- Retried
Array like, handles async
![Page 9: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/9.jpg)
So pretty much linq with async
![Page 10: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/10.jpg)
Rx.Observable.create( fnValue,, fnError,, fnCompleted )
Observable signature
![Page 11: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/11.jpg)
var stream = Rx.Observable.create((observer) =>{ })})
Emits
stream .subscribe( (data) => { console.log( data ); })
1
next()
observer.next(1);
2
next()
observer.next(2);
3
next()
observer.next(3);
![Page 12: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/12.jpg)
var stream = Rx.Observable.create((observer) =>{
})
stream .subscribe( (data) => { console.log( data ); } (err) => { console.log(err); })
Emits 1
next()
observer.next(1);
error message
error()
observer.error(‘something went wrong’)
![Page 13: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/13.jpg)
var stream = Rx.Observable.create((observer) =>{ })})stream .subscribe( (data) => { console.log( data ); } (err) => { console.log(err) }, () => { console.log(‘completed’) })
Emits 1
next()
observer.next(1);
complete()
observer.complete();
![Page 14: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/14.jpg)
Cancelling.unsubscribe()
![Page 15: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/15.jpg)
var homemadeStream = Rx.Observable.create((observer) => { var i=0;
});
var subscription2 = homemadeStream.subscribe((val) => { console.log('Homemade val',val);});
setTimeout(() => { console.log('Cancelling homemadeStream'); subscription2.unsubscribe();}, 1500); Calling dispose
Produce values till someone calls unsubscribevar handle = setInterval(() => {
observer.next( i++ ); }, 500);
Define whats to happen on unsubscribe
return function(){ console.log('Disposing timeout'); clearTimeout( handle ); }
![Page 16: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/16.jpg)
You will always create an observable from something
![Page 17: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/17.jpg)
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.fromPromise(promise)
Rx.Observable.fromIterable(function *() { yield 20 })
Rx.Observable.range(1,3)Rx.Observable.interval(miliseconds)
![Page 18: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/18.jpg)
Wrap an observable
next()error()
complete()
![Page 19: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/19.jpg)
var stream = Rx.Observable.create((observer) => { var request = new XMLHttpRequest();
request.open( ‘GET’, ‘url’ ); request.onload =() =>{ if(request.status === 200) { } else { } } request.onerror = () => { } request.send();})
stream.subscribe( )
observer.next( request.response );
(result) => { console.log( result ); }
Get our dataobserver.complete();
() => { console.log(‘completed’); }
No more data, close stream
observer.error( new Error( request.statusText ) )
(err) => { console.log(err) },
observer.error( new Error(‘unknown error’) );
Error
Error
![Page 20: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/20.jpg)
Hot vs Cold Observable
![Page 21: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/21.jpg)
Cold Observablerecorded tv show
Hot observableLive streaming
eg World Cup Final
![Page 22: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/22.jpg)
Observables are cold by default, unless you make them hot
var stream = Rx.Observable.interval(1000);
stream.subscribe((val) => { console.log('Subscriber 1', val);});
stream.subscribe((val) => { console.log('Subscriber 2', val); });
var stream = Rx.Observable .interval(1000) .publish();
stream.subscribe((val) => { console.log('Subscriber 1', val);});
setTimeout(function() { stream.connect(); }, 2000);
setTimeout(() => { stream.subscribe((val) => { console.log('Value', val); });}, 4000);
Subscriber 2 Values 0 1 2 3 4
Subscriber 1 Values 0 1 2 3 4
0 1 2 3 4 5 6
3 4 5 6
![Page 23: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/23.jpg)
You can create an observable from almost any
async conceptOperators however gives
it its power
Remember:
But:
![Page 24: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/24.jpg)
Operatorsmakes your code look like linq
![Page 25: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/25.jpg)
120+ operators Rxjs 460+ Rxjs 5
Combination Conditional
Multicasting Filtering
Transformation Utility
Categories
in production
![Page 26: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/26.jpg)
Marble diagramhow does that operator work
![Page 27: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/27.jpg)
Operator
Most operators are covered at rxmarbles.com
Stream 1 2 3
Other stream 4 5
Resulting stream 1 2 3 4 5
![Page 28: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/28.jpg)
Operator examplevar stream = Rx.Observable.of(1,2,3,4,5);stream
stream.subscribe((data) => { console.log(‘data’); })
Operators :map()filter()
3Emits
6
.map((val) => { return val + 1;})
changes the value
.filter((val) => { return val % 3 === 0;})
filters out values
![Page 29: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/29.jpg)
Dovar stream = Rx.Observable.of(1,2,3,4,5);
var subscription = stream
.filter(function(val){ return val % 2 === 0;});
subscription.subscribe(function(val){ console.log('Val',val);})
Echos every value without changing it,
used for logging.do((val) => { console.log('Current val', val);})
Current val 1Current val 2Current val 3Current val 4Current val 5Subscribe:
24
![Page 30: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/30.jpg)
debounce
var debounceTime = Rx.Observable.fromEvent(button,'click')
debounceTime.subscribe( function(){ console.log('mouse pressed');})
waits x ms and returns latest emitted
Ignores all generated mouse click events
for 2 seconds.debounce(2000);
Clicking save button2secclick click click click click
save()
![Page 31: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/31.jpg)
switchMapSwitch map,
complete something based on a condition
breakCondition = Rx.Observable.fromEvent(document,'click');breakCondition.switchMap((val) => { return Rx.Observable.interval(3000).mapTo(‘Do this');})
breakCondition.subscribe((val) => { console.log('Switch map', val);})
Intended action is completed/restarted by ‘breakCondition’
etc..
Do thisDo thisDo this
Do thisDo this
click
click
![Page 32: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/32.jpg)
source.subscribe((data) => { console.log( data );})
flatMaplet source = Rx.DOM.getJSON( 'data2.json' )
return Rx.Observable.fromArray( data ).map((row) => { return row.props.name; }); return observable
.flatMap((data) => {
} );
We get an array response that we want to emit row by row
We use flatMap instead of map because :We want to flatten our list to one stream
![Page 33: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/33.jpg)
flatMap explainedwhen you create a list of observables flatMap flattens that list so it becomes one stream
Great when changing from one type of stream to another
Without it you would have to listen to every single substream, would be messy
event
event
event
event
ajax ajax ajax ajax
json json json json
flatMap
map
![Page 34: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/34.jpg)
Problem : Autocomplete
Listen for keyboard pressesFilter so we only do server trip after x number of chars are enteredDo ajax call based on filtered input
Cash responses, don’t do unnecessary calls to http server
![Page 35: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/35.jpg)
The procedural approach
![Page 36: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/36.jpg)
let input = $(‘#input’);input.bind(‘keyup’,() = >{ let val = input.val() if(val.length >= 3 ) { if( isCached( val ) ) { buildList( getFromCache(val) ); return; }
doAjax( val ).then( (response) => { buildList( response.json() ) storeInCache( val, response.json() ) }); }})
fetch if x characters long
return if cached
do ajax
Ok solution but NOT so fluentWe need 3 methods to deal with cache
![Page 37: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/37.jpg)
The observable approach
![Page 38: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/38.jpg)
Stream modeling
key
key
key
key
key
key
FILTER
AJAX CALL
json
json
MAP
key
key
key
key
key
key
key
response
response
![Page 39: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/39.jpg)
flatmapExample = Rx.Observable.fromEvent(input,'keyup')
flatmapExample.subscribe( (result) =>{ console.log('Flatmap', result); buildList( result ) })
more fluent
Transform event to char.map((ev) => { return ev.target.value;})
Wait until we have 3 chars.filter(function(text){ return text.length >=3;})
Only perform search if this ‘search’ is unique.distinctUntilChanged()Excellent to use when coming from one stream to another
.switchMap((val) => { return Rx.DOM.getJSON( 'data3.json' );})
![Page 40: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/40.jpg)
Error handlingwhen streams fail
![Page 41: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/41.jpg)
errorcompletion
.catch() completion
completion
no values
completion
values, WIN!
.catch()
merge
.catch()merge
![Page 42: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/42.jpg)
onErrorResumeNext
completionvalues, WIN!
errorcompletion
no values
![Page 43: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/43.jpg)
retrylet stream = Rx.Observable.interval(1000)
.take(6);
.map((n) => { if(n === 2) { throw 'ex'; } return n;})
Produce error
.retry(2)Number of tries
before hitting error callback
stream.subscribe((data) => console.log(data)(error) => console.log(error)
1Emits
3
Makes x attempts before error cb is called
![Page 44: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/44.jpg)
retryWhendelay between attempts
let stream = Rx.Observable.interval(1000)
.take(6);
delay, 200 ms .retryWhen((errors) => { return errors.delay(200); })
.map((n) => { if(n === 2) { throw 'ex'; } return n;})
produce an error when= 2
stream.subscribe((data) => console.log(data)(error) => console.log(error)for those shaky connections
![Page 45: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/45.jpg)
What did we learn so far?
We can cancel with .unsubsribe()We can retry easily
A stream generates a continuous stream of valuesOperators manipulate either the values or the stream/s
We can “patch” an erronous stream with a .catch()orIgnore a failing stream altogether with onErrorResumeNext
![Page 46: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/46.jpg)
Schedulersbending time
![Page 47: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/47.jpg)
What about schedulers and testing?
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
![Page 48: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/48.jpg)
Schedulerstesting
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);console.log('Should equal', result === 4);
increment operatortestScheduler.advanceBy(1000);testScheduler.advanceBy(1000);testScheduler.advanceBy(1000);
assertconsole.log('Should equal', result === 2);
![Page 49: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/49.jpg)
Comparing promises to Rxjs
![Page 50: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/50.jpg)
.then vs .subscribe
getData().then( )
getData().subscribe( )
I will keep on streaming values
(data) => console.log(data),(data) => console.log(data),
(err) => console.log(err) (err) => console.log(err)
![Page 51: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/51.jpg)
user
order
orderItem
Fetch user
Then fetch order
Lastly fetch order item
![Page 52: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/52.jpg)
Cascading callsResponse:
//getUser
stream
.subscribe((orderItem) => { console.log('OrderItem',orderItem.id);})
{ id: 11, userId : 1 }.then(getOrderByUser)
.switchMap((user) => { //getOrder return Rx.Observable.of({ id : 11, userId : user.id }).delay(3000) })
{ id: 123, orderId : 11 }.then(getOrderItemByOrder)
.switchMap((order) => { //getOrderItem return Rx.Observable.of({ id: 114, orderId: order.id })})
{ id: 1 }getUser()
var stream = Rx.Observable.of({ id : 1 });
So we can see the first user observable being dropped when user 2 is emitted
![Page 53: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/53.jpg)
Short word on switchMap
This is to ensure we throw away the other calls when a new user is emitted
We don’t wantgetUsergetOrderByUsergetOrderItemByOrder
to complete if a new user is emitted
1 2 3
2 4 5
Not continuedReplaces above
stream
![Page 54: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/54.jpg)
user
orders messages
Fetch user
Fetch in parallell
![Page 55: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/55.jpg)
Cascading callwait for the first
.subscribe( (data) => { console.log( 'orders', data[0] ); console.log( 'messages', data[0] ); })
var stream = Rx.Observable.of([{ id : 1 }, { id : 2 }]);
getUser()We wait for user
function getOrdersAndMessages(user){return Promise.all([ getOrdersByUser( user.id ), getMessagesByUser( user.id )])
}
.then(getOrdersAndMessages)
stream.switchMap((user) => { return Rx.Observable.forkJoin( Rx.Observable.of([ { id: 1, userId : user.id } ]).delay(500), // orders Rx.Observable.of([ { id: 100, userId : user.id } ]).delay(1500) //messages )})
Calls to orders and message can happen in parallel
Orders,Messagesarrive at the same time
![Page 56: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/56.jpg)
Last summaryWe can use schedulers to easily test our code
Cascading calls can easily be setup
switchMap over flatMap when doing ajax callsbecause we need it to abandon the stream if
the first condition change
![Page 57: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/57.jpg)
Rxjs An elegant weapon for a
more civilized age
Remember..
![Page 58: Rxjs ngvikings](https://reader036.vdocument.in/reader036/viewer/2022070510/58ac27d41a28abf03a8b6065/html5/thumbnails/58.jpg)
Thank you