Rethink Async With RXJS
-
Upload
ryan-anklam -
Category
Technology
-
view
1.397 -
download
6
description
Transcript of Rethink Async With RXJS
Rethink Async with RXJS
Before We Begin
Before We Begin
Be KIND to those of us with OCD and…
Before We Begin
Change those Dropbox icons to B&W!
Reactive Programming!
Reactive Programming
Java…
Groovy…and JavaScript
At Netflix do a lot of RX…
.NET…
We Build This With RXJS
Reactive Programming
What if?
The iterator pattern and observer met at a bar, !fell in love, got married, and had a baby?
Reactive Programming
Reactive Programming
What if?
This baby would allow you to use the same code to !respond to events and asynchronous operations?
Meet Observable
Observables
What to events and async functions have in common?
Observables
As a mouse moves, doesn’t it just emit coordinates?
Tracking Mouse Movements
Observables
Tracking Mouse Movements
1s 2s
Mousedown
0s
Mouseup
{x : 200, y: 521} {x : 240, y: 552} {x : 210, y: 602} {x : 199, y: 579}{x : 221, y: 530} {x : 218, y: 570} {x : 200, y: 599}
Observables
var mouseMoves = Observable.fromEvent( mouseDown ) .takeUntil( mouseUp ) .map( function( mouseEvent ){ return { x : mouseEvent.clientX, y : mouseEvent.clientY }; } ) ; !mouseMoves.subscribe( function( cords ){ //do stuff for each mouse move cord });
Mouse Moves Observable
Observables
Observables Everywhere!• Events!• AJAX Requests!• Node Functions!• Arrays!• Promises!• And more….
Observables
Why not another Async approach?
• Replayable!• Composable!• Cancellable!• Cache-able!• Shareable!• Can emit multiple value-able
Functional Review!(With an RX twist)
Functional Review
Map
ReduceFilter
Zip
var searchResultsSets = keyups. map( function( key ){ return Observable.getJSON('/search?' + input.value) });
Functional Review
Map - Transforms data
var searchResultsSets = keyups. filter( function ( key ){ return input.value.length > 1; }). map( function( key ){ return Observable.getJSON('/search?' + input.value) );
Functional Review
Filter - Narrows Collections
var html = searchResultsSets. reduce(function( prev,curr ) { return prev + '<li>' + curr.name + '</li>'; },'' );
Functional Review
Reduce - Turns a collection into a single value
Initial prev value.
Functional Review
Zip - Combines two collections
var movies = [ 'Super Troopers', 'Pulp Fiction', 'Fargo' ] ; var boxArts =[ '/cdn/23212/120x80', '/cdn/73212/120x80','/cdn/99212/120x80' ] ; !var withArt = Observable. zip(movies, boxarts, function(movie, boxart){ return {title : movie, boxart : boxart}; }); !//[{title : 'Super Troo…', boxart : '/cdn…'}, // {title : 'Pulp Fict…', boxart : '/cdn…' }, // {title : 'Fargo', boxart : 'cdn…' } ]
Functional Review
Observable Data Streams Are Like Crazy Straws
keyPresses Observable
subscribe
throttlefilter
mapdistinctUntilChanged
reduce
Thinking Functionally
Thinking Functionally
Replace loops with map.
searchResults = Observable. getJSON(‘/people?’ + input.value); !searchResults. forEach( function( reps ){ var names = []; for( var i = 1; i < resp; i++ ){ var name = data[i].fName + ' ' + data[i].lName; names.push( name ); } });
Thinking Functionally
Replace loops with map and reduce.
searchResults = Observable. getJSON(‘/people?’ + input.value). map( function( data ){ return data.fName + data.lName; } ); !searchResults.forEach( function( resp ){ //resp will now be the names array })
Thinking Functionally
Replace if’s with filters.
var keyPresses = O.fromEvent( el, 'keyup' ) !keyPresses.forEach( function( e ){ if( e.which === keys.enter ){ //=> no! //do something } });
Thinking Functionally
Replace if’s with filters.
var enterPresses = O.fromEvent( el, 'keyup' ) .filter( function( e ){ return e.which && e.which === keys.enter; }); !enterPresses.forEach( function( e ){ //do something });
Thinking Functionally
Don’t put too much in a single stream.
var submits = O.fromEvent(input,'keypresses'). throttle(). map( function( e ){ return e.which } ). filter( function( key ){ return key === keys.enter || keys.escape; }). map(). reduce(). ...
Smaller streams are OK.
Thinking Functionally
var keys = Observable.fromEvent(input,'keypresses'). throttle(). map( function( e ){ return e.which } ); !var enters = keys.filter( function( key ) ){ return e.which === keys.enter; } !var escapes = keys.filter( function( key ) ){
Don’t put too much in a single stream.Smaller streams are OK.
Flattening Patterns!managing concurrency!
Observables = Events Over Time
Key presses over time…
.5s 1s 1.5s 2s 2.5s 3s
B R E A K IJ <- N G B A D
Observables = Events Over Time
1s 2s 3s 4s 5s 6s
BR
BRE
BREAK
BREAKJ
BREAKING
BREAKING BA
BREAKING BAD
Ajax requests over time…
The Three Musketeers
Goofy as merge
Donald as concat
Mickey as switchLatest
Starring
Flattening Patterns
merge - combines items in a collection as each item arrives
concat - combines collections in the order they arrived
switchLatest - switches to the latest collection that ! arrives
Concat
1s 2shttp://jsbin.com/fejod/4/edit
!
!
data
data
data
data
SwitchLatest
!
1s 2s
data
data
data
data
Building Animated AutoComplete!
Putting Everything Together
Animated Autocomplete
SearchBBrBreaking Bad Bridezillas Brothers & Sisters The Breakfast Club Brother Bear
Breaking Bad The Breakfast Club Breakout Kings Breaking Dawn Breaking Amish
BreBreaBreak
Observables = Events Over Time
Simple Widget, High Complexity• Respond to key presses
• Send off Ajax requests
• Animate out when search results become invalid
• Animate in when new search results come in
• Don’t show old results
• Make sure one animation is finished before starting another
var keyups = Observable. fromEvent( searchInput, 'keypress');
Animated Autocomplete
var searchResultsSets = keyups. filter( function ( e ){ return input.value.length > 1; }). map( function( e ){ return Observable. getJSON('/search?' + input.value); }). switchLatest();
Animated Autocomplete
var animateOuts = keyups. map(function( resultSet ){ return animateOut(resultsDiv); }); !var animateIns = searchResultsSets. map( function( resultSet ){ return Observable. of(resultsSet). concat(animateIn(resultsDiv)); });
Animated Autocomplete
var resultSets = animateOuts. merge(animateIns). concatAll(); !resultSets. forEach( function( resultSet ){ if (resultSet.length === 0) { $('.search-results').addClass('hidden'); } else { resultsDiv.innerHTML = toHTML(resultSet ); } } );
Animated Autocomplete
var keyups = Observable. fromEvent( searchInput, ‘keypress'); !var searchResultsSets = keyups. filter( function ( e ){ return input.value.length > 1; }). map( function( e ){ return Observable. getJSON('/search?' + input.value); }). switchLatest(); var animateOuts = keyups. map(function( resultSet ){ return animateOut(resultsDiv); }); !var animateIns = searchResultsSets. map( function( resultSet ){ return Observable. of(resultsSet). concat(animateIn(resultsDiv)); }); !var resultSets = animateOuts. merge(animateIns). concatAll(); !resultSets. forEach( function( resultSet ){ if (resultSet.length === 0) { $('.search-results').addClass('hidden'); } else { resultsDiv.innerHTML = toHTML(resultSet ); } } );
Animated Autocomplete
In Conclusion
• Observables are a POWERFUL abstraction!• Requires a bit of mental rewiring!• The RX API is HUGE, take baby steps!• Merging strategies are the key coordinating async!• Compose streams of data from small streams
To Find Out More
• http://github.com/jhusain/learnrx!• https://github.com/Reactive-Extensions/RxJS/tree/master/doc!• Chat with me
Questions?!