SlideShare a Scribd company logo
Rxjs
everything is a stream
Christoffer Noring
Google Developer Expert
@chris_noring
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
nce upon a time in async land
There were callbacks
Callbacks turned into callback hell
Promises to the rescue
service
.getData()
.then(getMoreData)
.then(getEvenMore)
.then(andSomeMore)
Looks great right?
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
Observables to the rescue
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
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
Manual creation of an
Observable
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);
let 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’)
let 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();
Subscribe to an Observable
let subscription =
stream$.subscribe(
fnValue,,
fnError,,
fnCompleted
)
Cancelling
.unsubscribe()
let subscription = Rx.Observable.interval(1000)
.subscribe((data) => console.log(data))
subscription.unsubscribe();
Define a dispose function1
2
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 unsubscribe
var handle = setInterval(() => {
observer.next( i++ );
}, 500);
Define whats to happen
on unsubscribe
return function(){
console.log('Disposing timeout');
clearTimeout( handle );
}
You will always create
an observable from something
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)
Wrap an observable
next()
error()
complete()
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 data
observer.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
Hot vs Cold Observable
Cold Observable
recorded tv show
Hot observable
Live streaming
eg World Cup Final
Observables are cold by default,
unless you make them hot
0 1 2 3 4
3 4
publisher$.connect();let publisher$ = Rx.Observable
.interval(1000)
.take(5)
.publish();
publisher$.subscribe(
data => console.log('subscriber from first minute',data),
err => console.log(err),
() => console.log('completed')
)
setTimeout(() => {
publisher$.subscribe(
data => console.log('subscriber from 2nd minute', data),
err => console.log(err),
() => console.log('completed')
)
}, 3000)
1
2
Warm Observables
waiting for someone to subscribe
let obs = Rx.Observable.interval(1000).take(3).publish().refCount();
setTimeout(() => {
obs.subscribe(data => console.log('sub1', data));
},1000)
setTimeout(() => {
obs.subscribe(data => console.log('sub2', data));
},2000)
Values begin emitting here
Receives values based on
where producer
is at, i.e hot
Share operator
flips between hot and col
let stream$ = Rx.Observable.create((observer) => {
observer.next( 1 );
observer.next( 2 );
observer.next( 3 );
observer.complete();
}).share()
1) Becomes a Hot Observable
An Observable has not completed when a
new subscription comes and subscribers > 0
2) Reverts to Cold Observable
Number of subscribers becomes 0 before a new subscription takes place. I.e a sce
No subscribers left
Not done yet
3) Reverts to Cold Observable
when an Observable completed before a new subscription
Already done
Hot vs Cold Summary
Hot shares values between subscribers AND
ubscriber receives values depending on where the Producer is cu
HOT
COLD
Everyone has their own producer of values
publish() + connect()
You can create an observable
from almost any async concept
Operators however gives it
its power
Remember:
But:
Operators
makes your code look like linq
120+ operators Rxjs 4
60+ Rxjs 5
Combination
Conditional
Multicasting
Filtering
Transformation
Utility
Categories
in production
Marble diagram
how does that operator work
Operator
Most operators are covered at rxmarbles.com
Stream 1 2 3
Other stream 4 5
Resulting stream 1 2 3 4 5
Operator example
var stream = Rx.Observable.of(1,2,3,4,5);
stream
stream.subscribe((data) => { console.log(‘data’); })
Operators :
map()
filter()
3
Emits
6
.map((val) => {
return val + 1;
})
changes the value
.filter((val) => {
return val % 3 === 0;
})
filters out values
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
sample
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.sampleTime(2000);
Clicking save button
2secclick click click click click
save()
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
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
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
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
The procedural approach
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
The observable approach
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
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' );
})
Error handling
when streams fail
error
completion
.catch() completion
completion
no values
completion
values, WIN!
.catch()
merge
.catch()
merge
onErrorResumeNext
completion
values, WIN
error
completion
no values
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
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) for those shaky connections
What did we learn so far?
We can cancel with .unsubsribe()
We can retry easily
A stream generates a continuous stream of values
Operators manipulate either the values or the stream/s
We can “patch” an erronous stream with a .catch()
or
Ignore a failing stream altogether
with onErrorResumeNext
Schedulers
bending time
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
Marble testing
Yes it has to do with marbles,
essentially its visual comparison
QUnit.test("Test simple emit 1 2 3", function(assert){
// setup
const lhsMarble = '-x-y-z';
const expected = '-x-y-z';
const expectedMap = {
x: 1,
y: 2,
z : 3
};
const lhs$ = testScheduler.createHotObservable(lhsMarble, expectedMap);
const myAlgorithm = ( lhs ) =>
Rx.Observable
.from( lhs );
const actual$ = myAlgorithm( lhs$ );
//assert
testScheduler.expectObservable(actual$).toBe(expected, expectedMap);
testScheduler.flush();
});
Create a hot observable from
a marble pattern
QUnit.test("Test filter", function(assert){
const lhsMarble = '-x-y-z';
const expected = '---y-';
const expectedMap = {
x: 1,
y: 2,
z : 3
};
const lhs$ = testScheduler.createHotObservable(lhsMarble,expectedMap);
const myAlgorithm = ( lhs ) =>
Rx.Observable
.from( lhs )
.filter(x => x % 2 === 0 );
const actual$ = myAlgorithm( lhs$ );
//assert
testScheduler.expectObservable(actual$).toBe(expected, expectedMap);
testScheduler.flush();
});
An extra hyphen,
to make time match
Yes -
means something
Its a time increment
There are other characters
besides - like
# = error and | = completion
QUnit.test("Test error", function(assert){
const lhsMarble = '-#';
const expected = '#';
const expectedMap = {
};
//const lhs$ = testScheduler.createHotObservable(lhsMarble, expectedMap);
const myAlgorithm = ( lhs ) =>
Rx.Observable
.from( lhs );
const actual$ = myAlgorithm( Rx.Observable.throw('error') );
//assert
testScheduler.expectObservable(actual$).toBe(expected, expectedMap);
testScheduler.flush();
})
Will cause an error
Error will happen
Test Summary
You use a descriptive marble to define behaviour
-x-y-z
There are symbols that mean something like :
-
#
|
Comparing promises
to Rxjs
.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)
user
order
orderItem
Fetch user
Then fetch order
Lastly fetch order item
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
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
user
orders messages
Fetch user
Fetch in parallell
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
Last summary
We can use schedulers to easily test our code
Cascading calls can easily be setup
switchMap over flatMap when doing ajax calls
because we need it to abandon the stream if
the first condition change
Further Reading
angular.io/resources Rxjs Ultimate
https://github.com/ReactiveX/rxjs
Free book
Official docs
Thank you

More Related Content

PDF
Rxjs vienna
PPTX
Rxjs swetugg
PDF
React lecture
PPTX
Rxjs ngvikings
PPTX
Firebase ng2 zurich
PPTX
Angular2 rxjs
PPTX
Typescript barcelona
PDF
Quick start with React | DreamLab Academy #2
Rxjs vienna
Rxjs swetugg
React lecture
Rxjs ngvikings
Firebase ng2 zurich
Angular2 rxjs
Typescript barcelona
Quick start with React | DreamLab Academy #2

What's hot (20)

PDF
Migrating from Flux to Redux. Why and how.
PDF
Nativescript angular
PPTX
Angular mix chrisnoring
PDF
Intro to Redux | DreamLab Academy #3
PDF
Dpilot Source Code With ScreenShots
PDF
Source Code for Dpilot
PDF
Recompacting your react application
PDF
Async JavaScript in ES7
PDF
Oop assignment 02
PPTX
Async Redux Actions With RxJS - React Rally 2016
PDF
Practical RxJava for Android
PDF
Advanced Akka For Architects
PDF
PDF
Ngrx slides
PDF
Innovative Specifications for Better Performance Logging and Monitoring
PDF
MongoDB World 2019: Life In Stitch-es
PDF
React table tutorial project setup, use table, and usefilter
PPTX
Durable functions
PDF
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
PDF
Inversion Of Control
Migrating from Flux to Redux. Why and how.
Nativescript angular
Angular mix chrisnoring
Intro to Redux | DreamLab Academy #3
Dpilot Source Code With ScreenShots
Source Code for Dpilot
Recompacting your react application
Async JavaScript in ES7
Oop assignment 02
Async Redux Actions With RxJS - React Rally 2016
Practical RxJava for Android
Advanced Akka For Architects
Ngrx slides
Innovative Specifications for Better Performance Logging and Monitoring
MongoDB World 2019: Life In Stitch-es
React table tutorial project setup, use table, and usefilter
Durable functions
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
Inversion Of Control
Ad

Similar to Rxjs marble-testing (20)

PPTX
PPTX
RxJS and Reactive Programming - Modern Web UI - May 2015
PPTX
Functional Reactive Programming (FRP): Working with RxJS
PPTX
RxJS In-Depth - AngularConnect 2015
PDF
Observables in Angular
PPTX
Introduction to RxJS
PDF
Reactive programming in Angular 2
PDF
RxJS - The Reactive extensions for JavaScript
PPTX
Angular observables for fun and profit
PDF
Rxjs kyivjs 2015
PPTX
Functional Reactive Programming with RxJS
PPTX
Luis Atencio on RxJS
PPTX
Rx – reactive extensions
PPTX
Intro to Functional Programming with RxJava
PDF
Reactive x
PPTX
Reactive programming with rx java
PDF
My Gentle Introduction to RxJS
PPTX
From zero to hero with the reactive extensions for JavaScript
PDF
RxJava@Android
PDF
RxJava@DAUG
RxJS and Reactive Programming - Modern Web UI - May 2015
Functional Reactive Programming (FRP): Working with RxJS
RxJS In-Depth - AngularConnect 2015
Observables in Angular
Introduction to RxJS
Reactive programming in Angular 2
RxJS - The Reactive extensions for JavaScript
Angular observables for fun and profit
Rxjs kyivjs 2015
Functional Reactive Programming with RxJS
Luis Atencio on RxJS
Rx – reactive extensions
Intro to Functional Programming with RxJava
Reactive x
Reactive programming with rx java
My Gentle Introduction to RxJS
From zero to hero with the reactive extensions for JavaScript
RxJava@Android
RxJava@DAUG
Ad

More from Christoffer Noring (19)

PPTX
Azure signalR
PPTX
Game dev 101 part 3
PPTX
Game dev 101 part 2
PPTX
Game dev workshop
PPTX
Deploying your static web app to the Cloud
PPTX
IaaS with ARM templates for Azure
PPTX
Learning Svelte
PPTX
PDF
Angular Schematics
PDF
Design thinking
PDF
Keynote ijs
PDF
Vue fundamentasl with Testing and Vuex
PDF
PDF
Graphql, REST and Apollo
PDF
Angular 2 introduction
PPTX
Angular modules in depth
PPTX
Finjs - Angular 2 better faster stronger
PPTX
Angular2 + rxjs
PPTX
Nativescript with angular 2
Azure signalR
Game dev 101 part 3
Game dev 101 part 2
Game dev workshop
Deploying your static web app to the Cloud
IaaS with ARM templates for Azure
Learning Svelte
Angular Schematics
Design thinking
Keynote ijs
Vue fundamentasl with Testing and Vuex
Graphql, REST and Apollo
Angular 2 introduction
Angular modules in depth
Finjs - Angular 2 better faster stronger
Angular2 + rxjs
Nativescript with angular 2

Recently uploaded (20)

PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
KodekX | Application Modernization Development
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Empathic Computing: Creating Shared Understanding
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Approach and Philosophy of On baking technology
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Encapsulation theory and applications.pdf
PDF
Modernizing your data center with Dell and AMD
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
NewMind AI Monthly Chronicles - July 2025
Per capita expenditure prediction using model stacking based on satellite ima...
NewMind AI Weekly Chronicles - August'25 Week I
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
The AUB Centre for AI in Media Proposal.docx
Network Security Unit 5.pdf for BCA BBA.
KodekX | Application Modernization Development
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Empathic Computing: Creating Shared Understanding
Understanding_Digital_Forensics_Presentation.pptx
The Rise and Fall of 3GPP – Time for a Sabbatical?
Approach and Philosophy of On baking technology
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Diabetes mellitus diagnosis method based random forest with bat algorithm
Encapsulation theory and applications.pdf
Modernizing your data center with Dell and AMD
Building Integrated photovoltaic BIPV_UPV.pdf
Review of recent advances in non-invasive hemoglobin estimation
Mobile App Security Testing_ A Comprehensive Guide.pdf

Rxjs marble-testing

  • 1. Rxjs everything is a stream Christoffer Noring Google Developer Expert @chris_noring
  • 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
  • 9. Manual creation of an Observable
  • 10. 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);
  • 11. let 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’)
  • 12. let 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();
  • 13. Subscribe to an Observable
  • 15. Cancelling .unsubscribe() let subscription = Rx.Observable.interval(1000) .subscribe((data) => console.log(data)) subscription.unsubscribe(); Define a dispose function1 2
  • 16. 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 unsubscribe var handle = setInterval(() => { observer.next( i++ ); }, 500); Define whats to happen on unsubscribe return function(){ console.log('Disposing timeout'); clearTimeout( handle ); }
  • 17. You will always create an observable from something
  • 18. 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)
  • 20. 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 data observer.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
  • 21. Hot vs Cold Observable
  • 22. Cold Observable recorded tv show Hot observable Live streaming eg World Cup Final
  • 23. Observables are cold by default, unless you make them hot 0 1 2 3 4 3 4 publisher$.connect();let publisher$ = Rx.Observable .interval(1000) .take(5) .publish(); publisher$.subscribe( data => console.log('subscriber from first minute',data), err => console.log(err), () => console.log('completed') ) setTimeout(() => { publisher$.subscribe( data => console.log('subscriber from 2nd minute', data), err => console.log(err), () => console.log('completed') ) }, 3000) 1 2
  • 24. Warm Observables waiting for someone to subscribe let obs = Rx.Observable.interval(1000).take(3).publish().refCount(); setTimeout(() => { obs.subscribe(data => console.log('sub1', data)); },1000) setTimeout(() => { obs.subscribe(data => console.log('sub2', data)); },2000) Values begin emitting here Receives values based on where producer is at, i.e hot
  • 25. Share operator flips between hot and col let stream$ = Rx.Observable.create((observer) => { observer.next( 1 ); observer.next( 2 ); observer.next( 3 ); observer.complete(); }).share()
  • 26. 1) Becomes a Hot Observable An Observable has not completed when a new subscription comes and subscribers > 0 2) Reverts to Cold Observable Number of subscribers becomes 0 before a new subscription takes place. I.e a sce No subscribers left Not done yet 3) Reverts to Cold Observable when an Observable completed before a new subscription Already done
  • 27. Hot vs Cold Summary Hot shares values between subscribers AND ubscriber receives values depending on where the Producer is cu HOT COLD Everyone has their own producer of values publish() + connect()
  • 28. You can create an observable from almost any async concept Operators however gives it its power Remember: But:
  • 29. Operators makes your code look like linq
  • 30. 120+ operators Rxjs 4 60+ Rxjs 5 Combination Conditional Multicasting Filtering Transformation Utility Categories in production
  • 31. Marble diagram how does that operator work
  • 32. Operator Most operators are covered at rxmarbles.com Stream 1 2 3 Other stream 4 5 Resulting stream 1 2 3 4 5
  • 33. Operator example var stream = Rx.Observable.of(1,2,3,4,5); stream stream.subscribe((data) => { console.log(‘data’); }) Operators : map() filter() 3 Emits 6 .map((val) => { return val + 1; }) changes the value .filter((val) => { return val % 3 === 0; }) filters out values
  • 34. 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
  • 35. sample 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.sampleTime(2000); Clicking save button 2secclick click click click click save()
  • 36. 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
  • 37. 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
  • 38. 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
  • 39. 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
  • 41. 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
  • 43. 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
  • 44. 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' ); })
  • 48. 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
  • 49. 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) for those shaky connections
  • 50. What did we learn so far? We can cancel with .unsubsribe() We can retry easily A stream generates a continuous stream of values Operators manipulate either the values or the stream/s We can “patch” an erronous stream with a .catch() or Ignore a failing stream altogether with onErrorResumeNext
  • 52. 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
  • 53. Marble testing Yes it has to do with marbles, essentially its visual comparison
  • 54. QUnit.test("Test simple emit 1 2 3", function(assert){ // setup const lhsMarble = '-x-y-z'; const expected = '-x-y-z'; const expectedMap = { x: 1, y: 2, z : 3 }; const lhs$ = testScheduler.createHotObservable(lhsMarble, expectedMap); const myAlgorithm = ( lhs ) => Rx.Observable .from( lhs ); const actual$ = myAlgorithm( lhs$ ); //assert testScheduler.expectObservable(actual$).toBe(expected, expectedMap); testScheduler.flush(); }); Create a hot observable from a marble pattern
  • 55. QUnit.test("Test filter", function(assert){ const lhsMarble = '-x-y-z'; const expected = '---y-'; const expectedMap = { x: 1, y: 2, z : 3 }; const lhs$ = testScheduler.createHotObservable(lhsMarble,expectedMap); const myAlgorithm = ( lhs ) => Rx.Observable .from( lhs ) .filter(x => x % 2 === 0 ); const actual$ = myAlgorithm( lhs$ ); //assert testScheduler.expectObservable(actual$).toBe(expected, expectedMap); testScheduler.flush(); }); An extra hyphen, to make time match
  • 56. Yes - means something Its a time increment
  • 57. There are other characters besides - like # = error and | = completion
  • 58. QUnit.test("Test error", function(assert){ const lhsMarble = '-#'; const expected = '#'; const expectedMap = { }; //const lhs$ = testScheduler.createHotObservable(lhsMarble, expectedMap); const myAlgorithm = ( lhs ) => Rx.Observable .from( lhs ); const actual$ = myAlgorithm( Rx.Observable.throw('error') ); //assert testScheduler.expectObservable(actual$).toBe(expected, expectedMap); testScheduler.flush(); }) Will cause an error Error will happen
  • 59. Test Summary You use a descriptive marble to define behaviour -x-y-z There are symbols that mean something like : - # |
  • 61. .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)
  • 62. user order orderItem Fetch user Then fetch order Lastly fetch order item
  • 63. 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
  • 64. 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
  • 66. 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
  • 67. Last summary We can use schedulers to easily test our code Cascading calls can easily be setup switchMap over flatMap when doing ajax calls because we need it to abandon the stream if the first condition change
  • 68. Further Reading angular.io/resources Rxjs Ultimate https://github.com/ReactiveX/rxjs Free book Official docs