60
Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Embed Size (px)

Citation preview

Page 1: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Async JavaScript at Netflix

Jafar Husain@jhusainMatthew Podwysocki@ReactiveX

Page 2: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Who is Jafar?Cross-Team Technical Lead for the Netflix UIs

Created the async data platform for Netflix UI’s

Member of TC39 13 years in the industry, formerly worked at Microsoft and GE

Page 3: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Who is Matthew Podwysocki? Not a member of TC39 Software Engineer at Microsoft Works with MS Open Tech on Reactive Extensions Organizes conferences such as RobotsConf, JSConf

Page 4: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

This is the story of how Netflix solved

BIG async problems

by thinking differently about

Events.

Page 5: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

2014321

Page 6: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

The Netflix App is AsynchronousApp StartupPlayerData AccessAnimationsView/Model binding

Page 7: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Async ProblemsMemory LeaksRace ConditionsCallback HellComplex state machinesError Handling

Page 8: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Async is Hardfunction play(movieId, cancelButton, callback) { var movieTicket, playError, tryFinish = function() { if (playError) { callback(null, playError); } else if (movieTicket && player.initialized) { callback(null, ticket); } }; cancelButton.addEventListener(“click”, function() { playError = “cancelled”; } if (!player.initialized) { player.init(function(error) { playError = error; tryFinish(); } } authorizeMovie(function(error, ticket) { playError = error; movieTicket = ticket; tryFinish(); });});

function play(movieId, cancelButton, callback) { var movieTicket, playError, tryFinish = function() { if (playError) { callback(null, playError); } else if (movieTicket && player.initialized) { callback(null, ticket); } }; cancelButton.addEventListener(“click”, function() { playError = “cancelled”; } if (!player.initialized) { player.init(function(error) { playError = error; tryFinish(); }); } authorizeMovie(function(error, ticket) { playError = error; movieTicket = ticket; tryFinish(); });});

function play(movieId, cancelButton, callback) { var movieTicket, playError, tryFinish = function() { if (playError) { callback(null, playError); } else if (movieTicket && player.initialized) { callback(null, ticket); } }; cancelButton.addEventListener(“click”, function() { playError = “cancelled”; } if (!player.initialized) { player.init(function(error) { playError = error; tryFinish(); }); } authorizeMovie(function(error, ticket) { playError = error; movieTicket = ticket; tryFinish(); });});

function play(movieId, cancelButton, callback) { var movieTicket, playError, tryFinish = function() { if (playError) { callback(null, playError); } else if (movieTicket && player.initialized) { callback(null, ticket); } }; cancelButton.addEventListener(“click”, function() { playError = “cancelled”; } if (!player.initialized) { player.init(function(error) { playError = error; tryFinish(); } } authorizeMovie(function(error, ticket) { playError = error; movieTicket = ticket; tryFinish(); });});

Page 9: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX
Page 10: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX
Page 11: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Iterator

Observer

?

Page 12: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Iterator> var iterator = getNumbers();

> { value: 1, done: false }>

> console.log(iterator.next());

> console.log(iterator.next());

> { value: 2, done: false }>> console.log(iterator.next());

> { value: 3, done: false }>> console.log(iterator.next());

> { done: true }>

Page 13: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Observer Pattern> document.addEventListener( “mousemove”, function next(e) { console.log(e); });

> { clientX: 425, clientY: 543 }> { clientX: 450, clientY: 558 }> { clientX: 455, clientY: 562 }> { clientX: 460, clientY: 743 }> { clientX: 476, clientY: 760 }> { clientX: 476, clientY: 760 }> { clientX: 476, clientY: 760 }> { clientX: 476, clientY: 760 }

Page 14: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Iterator Observerprogressively send information to consumer

Page 15: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX
Page 16: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

“What’s the difference between an Array…

[{x: 23, y: 44}, {x:27, y:55}, {x:27, y:55}]

Page 17: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

… and an Event?

{x: 23, y: 44}...{x:27, y:55}.... {x:27, y:55}......

Page 18: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Events and Arrays are both collections.

Page 19: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Now for a brief

JavaScript 6 tutorial…

Page 20: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Functions

x + 1function(x) { return x + 1; }x =>function(x, y) { return x + y; }(x,

y)x + y =>

JS65

Page 21: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Fin.

Page 22: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

The majority of Netflix’s async code is written with just a few

flexible functions.

Page 23: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

ForEach

> [1, 2, 3].forEach(x => console.log(x))> 1> 2> 3>

Page 24: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Map

Page 25: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Map

> [1, 2, 3].map(x => x + 1)> [2, 3, 4]>

Page 26: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Filter

Page 27: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Filter

> [1, 2, 3].filter(x => x > 1)> [2, 3]>

Page 28: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

concatAll

Page 29: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

concatAll

> [ [1], [2, 3], [], [4] ].concatAll()> [1, 2, 3, 4]>

Page 30: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Map/Filter/ConcatAll> [1, 2, 3].map(x => x + 1)> [2, 3, 4]

> [1, 2, 3].filter(x => x > 1)> [2, 3]

> [ [1], [2, 3], [], [4] ].concatAll()> [1, 2, 3, 4]>

Page 31: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX
Page 32: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Let’s use map, filter, and concatAll to get a list of your favorite Netflix titles.

Page 33: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Top-rated Movies Collection

var getTopRatedFilms = user => user.videoLists. map(videoList => videoList.videos. filter(video => video.rating === 5.0)). concatAll();

getTopRatedFilms(user). forEach(film => console.log(film));

Page 34: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

What if I told you……that you could create a drag event…

…with nearly the same code?

Page 35: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Top-rated Movies Collection

var getTopRatedFilms = user => user.videoLists. map(videoList => videoList.videos. filter(video => video.rating === 5.0)). concatAll();

getTopRatedFilms(user). forEach(film => console.log(film));

Page 36: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Mouse Drags Collection

var getElementDrags = elmt => elmt.mouseDowns. map(mouseDown => document.mouseMoves. filter takeUntil(document.mouseUps)). concatAll();

getElementDrags(image). forEach(pos => image.position = pos);

Page 37: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Observable === Collection + Time

Introducing Observable

Page 38: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Reactive ExtensionsObservable Type + Array Functions (and more)

Open SourcePorted to…

CC#/VB.Net Javascript Java (Netflix)

Page 39: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Observables can model…EventsAnimationsAsync IO

Page 40: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Events to Observables

var mouseMoves = Observable. fromEvent(element, “mousemove”);

Page 41: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Event Subscription

// “subscribe”var handler = (e) => console.log(e);document.addEventListener(“mousemoves”, handler);

// “unsubscribe”document.removeEventListener(“mousemoves”, handler);

Page 42: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Observable.forEach

// “subscribe”var subscription = mouseMoves.forEach(console.log);

// “unsubscribe”subscription.dispose();

Page 43: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Expanded Observable.forEach// “subscribe”var subscription = mouseMoves.forEach( // next data event => console.log(event), // error error => console.error(error), // completed () => console.log(“done”));

// “unsubscribe”subscription.dispose();

optional

Page 44: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Observable Literal

JS6

time

{1……2…………3}

Page 45: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

ForEach

> {1……2…………3}.forEach(console.log)

> 1>> 2>> 3>

time

> {1……2…………3}

Page 46: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Map

> {1……2…………3}.map(x => x + 1)

> 2>> 3>> 4>

time

Page 47: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

> 2> 3>

Filter

> {1……2…………3}.filter(x => x + 1)

> > 2>

time

Page 48: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

concatAll

[ [1] [2, 3], [], [4]].concatAll()

[1, 2, 3, 4]

Page 49: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

concatAll

{…{1}………{2………………3}, ……………{}………………{4}}.concatAll()

{…1…2………………3…4}

time

Page 50: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

TakeUntil

{…1…2…………3}.takeUntil({……………4})

{…1…2…}

timeSource collection

Stop collection

Page 51: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Don’t unsubscribe from Events.

Complete them when another event fires.

Page 52: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Mouse Drags Collection

var getElementDrags = elmt => elmt.mouseDowns. map(mouseDown => document.mouseMoves. takeUntil(document.mouseUps)). concatAll();

getElementDrags(image). forEach(pos => image.position = pos);

Page 53: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Netflix Search

Page 54: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Netflix Searchvar searchResultSets = keyPresses. throttle(250). map(key => getJSON(“/searchResults?q=” + input.value). retry(3). takeUntil(keyPresses)). concatAll();

searchResultSets.forEach( resultSet => updateSearchResults(resultSet), error => showMessage(“the server appears to be down.”));

Page 55: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Netflix Player

Page 56: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Player Callback Hellfunction play(movieId, cancelButton, callback) { var movieTicket, playError, tryFinish = function() { if (playError) { callback(null, playError); } else if (movieTicket && player.initialized) { callback(null, ticket); } }; cancelButton.addEventListener(“click”, function() { playError = “cancel”; }); if (!player.initialized) { player.init(function(error) { playError = error; tryFinish(); } } authorizeMovie(movieId, function(error, ticket) { playError = error; movieTicket = ticket; tryFinish(); });});

Page 57: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Player with Observablevar authorizations = player. init(). map(() => playAttempts. map(movieId => player.authorize(movieId).

catch(e => Observable.empty). takeUntil(cancels)). concatAll())). concatAll(); authorizations.forEach( license => player.play(license), error => showDialog(“Sorry, can’t play right now.”));

Page 58: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Netflix: Observable EverywhereApp StartupPlayerData AccessAnimationsView/Model binding

Page 59: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Resources reactivetrader.azurewebsites.net https://github.com/Reactive-Extensions/RxJS RxJava http://jhusain.github.io/learnrx/ @jhusain @ReactiveX @MattPodwysocki

Page 60: Async JavaScript at Netflix Jafar Husain @jhusain Matthew Podwysocki @ReactiveX

Questions