rethink async with rxjs

53
Rethink Async with RXJS

Upload: devobjective

Post on 12-Apr-2017

117 views

Category:

Technology


0 download

TRANSCRIPT

Rethink Async with RXJS

Senior UI Engineer at

About Me

bittersweetryan

[email protected]

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

Merge

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

data

data

data

data

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!!

[email protected]@bittersweetryan!

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?!

Thank You.!!

[email protected]@bittersweetryan!