Deep Dive into RxJS
Javascript
- Single call stack
- Async handling of events
- Async handling of data sources
Callbacks
- Very much needed
- Context changes
- Bad readability
- Limited reuse
- Increased complexity when unstructured
var isLoaded = false;
$.ajax({
url:'http://blah.api.com',
success:function(resp){
isLoaded = true;
}
});
var isLoaded = false;
var isBlah1Loaded = false;
var isBlah2Loaded = false;
$.ajax({
url:'blah1',
success:function(resp){
isBlah1Loaded = true;
if(isBlah2Loaded){
isLoaded = true;
}
}
});
$.ajax({
url:'blah2',
success:function(resp){
isBlah2Loaded = true;
if(isBlah1Loaded){
isLoaded = true;
}
}
});
But there are promises!
Promise cons
- Processes when invoked.
- Not disposable
- What about event listener callbacks?
Something better?
Lets look at RxJS
A concept of Reactive extensions.
Java, c#, Python, Ruby, Scala
and Javascript!
Rx.js - A primer
- Reactive extensions
Reactive Observables
Collection of values over time is modeled as an Observable in Rx.js
Reactive Observables
Can model
- Events
- Data Requests
- Animations
Stream => Infinite length/lazily evaluated.
observable
observer
do stuff
onCompleted
onError
onNext
observer
side effects
side effects
var searchWikipedia = function(query){
var promise = $.ajax({
url:"https://en.wikipedia.org/w/api.php",
dataType:'jsonp',
data:{
action:"query",
format:"json",
list:"search",
srsearch:query
}
}).promise();
return Rx.Observable.fromPromise(promise);
}
Rx.Observable.from(['Led Zeppelin', 'The Kinks', 'CCR']);
Rx.Observable.create(function(observer){
try{
if(condition){
observer.onNext(42);
} else {
observer.onCompleted();
}
}catch(e){
observer.onError(e);
}
return disposeFn;
});
Rx.js - A primer
- A concept of reactive extensions.
- Api for Observable
Subscriber(observer)
var source = Rx.Observable.from(['Led Zeppelin', 'The Kinks', 'CCR']);
source.subscribe(function(value){
console.log(value)
}, function(error){
console.log('error')
}, function(){
console.log('done');
});
//OUTPUT
//Led Zeppelin
//The Kinks
//CCR
//done
Rx.js - A primer
- A concept of reactive extensions.
- Api for Observable
- Subscribe to an Observable
map
filter
flatMap
concatMap
sample
skip
takeUntil
take
delay
distinctUntilChanged
find
join
max
mergeAll
reduce
pluck
retry
throttle
debounce
transduce
zip
and many more...
Combinators!
Rx.js - A primer
- A concept of reactive extensions.
- Api for Observable
- Subscribe to an Observable
- Combinators
var queries = ['Led Zeppelin', 'Pink Floyd', 'The Kinks', 'Foo Fighters', 'Doobie Brothers'];
var queryObs = Rx.Observable.from(queries);
var source = queryObs
.flatMap(searchWikipedia)
.map(function(searchRes){ return searchRes.query.search[2].title; })
.zip(queries, function(s1, s2){
return s2 + ' - [Wiki response]' + s1;
});
var sub = source.subscribe(function(val){
console.log(val);
}, function(err){
console.log(err);
}, function(done){
console.log('done');
});
Xhr with Rx Observables
Observables
Reactive
Combinators
Functional
Map
Delay
Debounce
Autocomplete widget
var $input = $('#textInput');
// Get all distinct key up events from the input and only fire if long enough and distinct
var source = Rx.Observable.fromEvent($input, 'keyup')
.map(function (e) {
return e.target.value; // Project the text from the input
})
.filter(function (text) {
return text.length > 2; // Only if the text is longer than 2 characters
})
.debounce(750 /* Pause for 750ms */ )
.distinctUntilChanged() // Only if the value has changed
.flatMapLatest(searchWikipedia)
source.subscribe(function (data) {
updateResults(data);
},
function (error) {
showError();
});
Drag and Drop with Rxjs
var dragTarget = $('#dragTarget'),
mouseup = dragTarget.onAsObservable('mouseup'),
mousemove = dragTarget.onAsObservable('mousemove'),
mousedown = dragTarget.onAsObservable('mousedown')
.map(function (event) {
event.preventDefault();
return {
left: event.clientX - dragTarget.offset().left,
top: event.clientY - dragTarget.offset().top
};
}),
mousedrag = mousedown.flatMapLatest(function(imageOffset) {
return mousemove.select(function (pos) {
return {
left: pos.clientX - imageOffset.left,
top: pos.clientY - imageOffset.top
};
}).takeUntil(mouseup);
});
mousedrag.subscribe (function (pos) {
$('#dragTarget').css({top: pos.top, left: pos.left});
});
- Include rx.*.js
- rx.jquery.js
- Rx.Observable.create
- Rx.Observable.from
- Rx.Observable.fromEvent
- Rx.Observable.fromCallback
- Rx.Observable.fromNodeCallback
- Rx.Observable.fromPromise
Vanilla Js/Jquery/Node
- Cyclejs - http://cycle.js.org/
- Rx-react - https://github.com/fdecampredon/rx-react
- RxEmber- https://github.com/blesh/RxEmber
- Rx-Angular - https://github.com/Reactive-Extensions/rx.angular.js/
For your MV* Framework
- Hot and cold Observables
- Backpressure
- Structuring Rx code
Advanced stuff
- Declarative Programming
- Localising side effects
- Error Handling
- Composability
- Streams FTW!
- Robust
- Tried and tested
- Browser compatible
- Performant
Takeaways
Alternatives
FRP in production
Resources
Deep dive into Rxjs Observables
By Pavithra Kodmad
Deep dive into Rxjs Observables
- 3,022