2. 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
3. nce upon a time in async land
There were callbacks
Callbacks turned into callback hell
4. Promises to the rescue
service
.getData()
.then(getMoreData)
.then(getEvenMore)
.then(andSomeMore)
Looks great right?
5. But promises were flawed
No cancellation
eal with other async concepts like mouse positions, clicks, use
No rich composition
And brexit happened
Cumbersome to retry
Only returns one value
7. What is an observable
Observable is just a function
that takes an observer and returns a function
Observer: 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
8. Promise
vs Array
vs Observable
list
.map( x = > x.prop )
.filter( x => x > 2 )
.take( 2 )
Array
list
.map( x = > x.prop )
.filter( x => x > 2 )
.take( 2 )
.subscribe(
x => console.log(x),
err => console.log(err) )
Observable
Promise
service.get()
.then( x => console.log(x) )
.catch( err => console.log(err) ) but can also
- Cancelled
- Retried
Array like,
handles async
31. Do
var 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 1
Current val 2
Current val 3
Current val 4
Current val 5
Subscribe:
2
4
32. Delay
Delay,
the whole sequence
stream
.subscribe( (val) => {
var newTime = new Date().getTime();
console.log('Delayed', val + " " + (newTime - time));
})
Rx.Observable.merge(
).subscribe((val) => {
var newTime = new Date().getTime();
console.log('Merged Delay', val + " " + (newTime - time));
})
âŠ.. 1 second
1
2
3
âŠ.. 1 second
Marco
âŠ.. 2 second
Polo
.delay(1000)
Rx.Observable.of('Marco').delay(1000),
Rx.Observable.of('Polo').delay(2000)
40. buffer
let numbers = Rx.Observable.interval(1000);
let bufferBy = Rx.Observable.fromEvent(document,'click');
let buffered = numbers.buffer( bufferBy );
buffered.subscribe(
);
Numbers are generated to a buffer
until condition is met,
then the buffer content is emitted
Generate value to buffer
Release all values
(values) => { console.log(âBuffered',values);}
41. switchMap
Switch 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 this
Do this
Do this
Do this
Do this
click
click
42. source.subscribe((data) => {
console.log( data );
})
flatMap
let 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
43. flatMap explained
when you create a list of observables flatMap flattens that list s
Great when changing from one type of stream to another
Without it you would have to listen to every single substream, w
eve
nt
eve
nt
eve
nt
eve
nt
ajax ajax ajax ajax
json json json json
flatMap
map
44. Problem : Autocomplete
Listen for keyboard presses
Filter so we only do server trip after x number of
chars are entered
Do ajax call based on filtered input
Cash responses,
donât do unnecessary calls to http server
46. 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 fluent
We need 3 methods to deal with cache
48. Stream modeling
key key key key key key
FILTER
AJAX CALL
jso
n
jso
n
MAP
key key key key key key key
respons
e
respons
e
49. 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' );
})
54. Error
ignoring
You have several sources and two terminates
You want the other normal source to work
var errStream = Rx.Observable
.throw('AAA thump..');
var errStreamWithFlattenedData =
Rx.Observable
.interval(500)
.take(3)
.flatMap((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(
(val) =>{ console.log('error stream ignored', val);},
(err) => { console.log("error stream ignored, error",err);},
() => { console.log("completion of error stream ignoredâ);}
)
55. Error handling
retry
let 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)
1
Emits
3
Makes x attempts before error cb is called
56. Error handling
retryWhen, delay 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)
58. What about schedulers and
testing?
Because scheduler has its own virtual clock
Anything scheduled on that scheduler
will adhere to time denoted on the clock
I.e we can bend time for ex unit testing
59. Schedulers
testingvar 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 operator
testScheduler.advanceBy(1000);
testScheduler.advanceBy(1000);
testScheduler.advanceBy(1000);
assert
console.log('Should equal', result === 2);
62. Cascading calls
Response:
//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
63. Short word on switchMap
is to ensure we throw away the other calls when a new user is em
We donât want
getUser
getOrderByUser
getOrderItemByOrder
to complete if a new user is emitted
1 2 3
2 4 5
Not continued
Replaces above
stream
64. Cascading call
wait 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,Messages
arrive at the same time