rethink async with rxjs

53
Rethink Async with RXJS

Upload: ryan-anklam

Post on 17-Dec-2014

1.407 views

Category:

Technology


6 download

DESCRIPTION

This is an updated version of my "Add more fun to your functional programming with RXJS". It includes a bit more background information on Reactive programming.

TRANSCRIPT

Page 1: Rethink Async With RXJS

Rethink Async with RXJS

Page 2: Rethink Async With RXJS

Senior UI Engineer at

About Me

bittersweetryan

[email protected]

Page 3: Rethink Async With RXJS

Before We Begin

Page 4: Rethink Async With RXJS

Before We Begin

Be KIND to those of us with OCD and…

Page 5: Rethink Async With RXJS

Before We Begin

Change those Dropbox icons to B&W!

Page 6: Rethink Async With RXJS

Reactive Programming!

Page 7: Rethink Async With RXJS

Reactive Programming

Java…

Groovy…and JavaScript

At Netflix do a lot of RX…

.NET…

Page 8: Rethink Async With RXJS

We Build This With RXJS

Page 9: Rethink Async With RXJS

Reactive Programming

What if?

The iterator pattern and observer met at a bar, !fell in love, got married, and had a baby?

Page 10: Rethink Async With RXJS

Reactive Programming

Page 11: Rethink Async With RXJS

Reactive Programming

What if?

This baby would allow you to use the same code to !respond to events and asynchronous operations?

Page 12: Rethink Async With RXJS

Meet Observable

Page 13: Rethink Async With RXJS

Observables

What to events and async functions have in common?

Page 14: Rethink Async With RXJS

Observables

As a mouse moves, doesn’t it just emit coordinates?

Tracking Mouse Movements

Page 15: Rethink Async With RXJS

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}

Page 16: Rethink Async With RXJS

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

Page 17: Rethink Async With RXJS

Observables

Observables Everywhere!• Events!• AJAX Requests!• Node Functions!• Arrays!• Promises!• And more….

Page 18: Rethink Async With RXJS

Observables

Why not another Async approach?

• Replayable!• Composable!• Cancellable!• Cache-able!• Shareable!• Can emit multiple value-able

Page 19: Rethink Async With RXJS

Functional Review!(With an RX twist)

Page 20: Rethink Async With RXJS

Functional Review

Map

ReduceFilter

Zip

Page 21: Rethink Async With RXJS

var searchResultsSets = keyups. map( function( key ){ return Observable.getJSON('/search?' + input.value) });

Functional Review

Map - Transforms data

Page 22: Rethink Async With RXJS

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

Page 23: Rethink Async With RXJS

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.

Page 24: Rethink Async With RXJS

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…' } ]

Page 25: Rethink Async With RXJS

Functional Review

Observable Data Streams Are Like Crazy Straws

keyPresses Observable

subscribe

throttlefilter

mapdistinctUntilChanged

reduce

Page 26: Rethink Async With RXJS

Thinking Functionally

Page 27: Rethink Async With RXJS

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 ); } });

Page 28: Rethink Async With RXJS

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 })

Page 29: Rethink Async With RXJS

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 } });

Page 30: Rethink Async With RXJS

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 });

Page 31: Rethink Async With RXJS

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.

Page 32: Rethink Async With RXJS

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.

Page 33: Rethink Async With RXJS

Flattening Patterns!managing concurrency!

Page 34: Rethink Async With RXJS

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

Page 35: Rethink Async With RXJS

Observables = Events Over Time

1s 2s 3s 4s 5s 6s

BR

BRE

BREAK

BREAKJ

BREAKING

BREAKING BA

BREAKING BAD

Ajax requests over time…

Page 36: Rethink Async With RXJS

The Three Musketeers

Goofy as merge

Donald as concat

Mickey as switchLatest

Starring

Page 37: Rethink Async With RXJS

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

Page 38: Rethink Async With RXJS

Merge

1s 2shttp://jsbin.com/wehusi/13/edit

data

data

data

data

Page 39: Rethink Async With RXJS

Concat

1s 2shttp://jsbin.com/fejod/4/edit

!

!

data

data

data

data

Page 40: Rethink Async With RXJS

SwitchLatest

!

1s 2s

data

data

data

data

Page 41: Rethink Async With RXJS

Building Animated AutoComplete!

Putting Everything Together

Page 42: Rethink Async With RXJS

Animated Autocomplete

SearchBBrBreaking Bad Bridezillas Brothers & Sisters The Breakfast Club Brother Bear

Breaking Bad The Breakfast Club Breakout Kings Breaking Dawn Breaking Amish

BreBreaBreak

Page 43: Rethink Async With RXJS

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

Page 44: Rethink Async With RXJS

var keyups = Observable. fromEvent( searchInput, 'keypress');

Animated Autocomplete

Page 45: Rethink Async With RXJS

var searchResultsSets = keyups. filter( function ( e ){ return input.value.length > 1; }). map( function( e ){ return Observable. getJSON('/search?' + input.value); }). switchLatest();

Animated Autocomplete

Page 46: Rethink Async With RXJS

var animateOuts = keyups. map(function( resultSet ){ return animateOut(resultsDiv); }); !var animateIns = searchResultsSets. map( function( resultSet ){ return Observable. of(resultsSet). concat(animateIn(resultsDiv)); });

Animated Autocomplete

Page 47: Rethink Async With RXJS

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

Page 48: Rethink Async With RXJS

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

Page 49: Rethink Async With RXJS

In Conclusion!!

[email protected]@bittersweetryan!

Page 50: Rethink Async With RXJS

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

Page 51: Rethink Async With RXJS

To Find Out More

• http://github.com/jhusain/learnrx!• https://github.com/Reactive-Extensions/RxJS/tree/master/doc!• Chat with me

Page 52: Rethink Async With RXJS

Questions?!

Page 53: Rethink Async With RXJS

Thank You.!!

[email protected]@bittersweetryan!