Asynchronous JavaScript at Netflix

56
Async JavaScript at Netflix Jafar Husain jhusain@netflix .com

description

Netflix's Jafar Husain shares how the company is using the Reactive Extensions (Rx) library to build responsive UIs across their device experiences.

Transcript of Asynchronous JavaScript at Netflix

Page 1: Asynchronous JavaScript at Netflix

Async JavaScript at Netflix

Jafar [email protected]

Page 2: Asynchronous JavaScript at Netflix

Who am I?Cross-Team Technical Lead for the Netflix UIs

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

6 years of experience building large systems with Functional Reactive Programming

Page 3: Asynchronous JavaScript at Netflix

This is the story of how Netflix solved

BIG async problems

by thinking differently about

Events.

Page 4: Asynchronous JavaScript at Netflix

2014321

Page 5: Asynchronous JavaScript at Netflix

Our App is AsynchronousApp StartupPlayerData AccessAnimationsView/Model binding

Page 6: Asynchronous JavaScript at Netflix

Async ProblemsMemory LeaksRace ConditionsCallback HellComplex state machinesError Handling

Page 7: Asynchronous JavaScript at Netflix

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 8: Asynchronous JavaScript at Netflix
Page 9: Asynchronous JavaScript at Netflix
Page 10: Asynchronous JavaScript at Netflix

Iterator

Observer

progressively send information to consumer

Page 11: Asynchronous JavaScript at Netflix
Page 12: Asynchronous JavaScript at Netflix

“What’s the difference between an Array…

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

Page 13: Asynchronous JavaScript at Netflix

… and an Event?

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

Page 14: Asynchronous JavaScript at Netflix

Events and Arrays are both collections.

Page 15: Asynchronous JavaScript at Netflix

Now for a brief

JavaScript 6 tutorial…

Page 16: Asynchronous JavaScript at Netflix

Functions

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

y)x + y =>

JS65

Page 17: Asynchronous JavaScript at Netflix

Fin.

Page 18: Asynchronous JavaScript at Netflix

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

flexible functions.

Page 19: Asynchronous JavaScript at Netflix

ForEach

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

Page 20: Asynchronous JavaScript at Netflix

Map

Page 21: Asynchronous JavaScript at Netflix

Map

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

Page 22: Asynchronous JavaScript at Netflix

Filter

Page 23: Asynchronous JavaScript at Netflix

Filter

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

Page 24: Asynchronous JavaScript at Netflix

concatAll

Page 25: Asynchronous JavaScript at Netflix

concatAll

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

Page 26: Asynchronous JavaScript at Netflix

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 27: Asynchronous JavaScript at Netflix
Page 28: Asynchronous JavaScript at Netflix

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

Page 29: Asynchronous JavaScript at Netflix

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 30: Asynchronous JavaScript at Netflix

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

…with nearly the same code?

Page 31: Asynchronous JavaScript at Netflix

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 32: Asynchronous JavaScript at Netflix

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 33: Asynchronous JavaScript at Netflix

Observable === Collections + Time

Introducing Observable

Page 34: Asynchronous JavaScript at Netflix

Reactive ExtensionsObservable Type + Array Functions (and more)

Open SourcePorted to…

CC#/VB.Net Javascript Java (Netflix)

Page 35: Asynchronous JavaScript at Netflix

Observables can model…EventsData requestsAnimations

Page 36: Asynchronous JavaScript at Netflix

Events to Observables

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

Page 37: Asynchronous JavaScript at Netflix

Event Subscription

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

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

Page 38: Asynchronous JavaScript at Netflix

Observable.forEach

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

// “unsubscribe”subscription.dispose();

Page 39: Asynchronous JavaScript at Netflix

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 40: Asynchronous JavaScript at Netflix

Observable Literal

JS6

time

{1……2…………3}

Page 41: Asynchronous JavaScript at Netflix

ForEach

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

> 1>> 2>> 3>

time

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

Page 42: Asynchronous JavaScript at Netflix

Map

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

> 2>> 3>> 4>

time

Page 43: Asynchronous JavaScript at Netflix

> 2> 3>

Filter

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

> > 2>

time

Page 44: Asynchronous JavaScript at Netflix

concatAll

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

[1, 2, 3, 4]

Page 45: Asynchronous JavaScript at Netflix

concatAll

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

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

time

Page 46: Asynchronous JavaScript at Netflix

TakeUntil

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

{…1…2…}

timeSource collection

Stop collection

Page 47: Asynchronous JavaScript at Netflix

Don’t unsubscribe from Events.

Complete them when another event fires.

Page 48: Asynchronous JavaScript at Netflix

Mouse Drags Collection

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

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

Page 49: Asynchronous JavaScript at Netflix

Netflix Search

Page 50: Asynchronous JavaScript at Netflix

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 51: Asynchronous JavaScript at Netflix

Netflix Player

Page 52: Asynchronous JavaScript at Netflix

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 53: Asynchronous JavaScript at Netflix

Player with Observablevar authorizations = player. init(). map(() => playAttempts. map(movieId => player.authorize(movieId). retry(3). takeUntil(cancels)). concatAll())). concatAll(); authorizations.forEach( license => player.play(license), error => showDialog(“Sorry, can’t play right now.”));

Page 54: Asynchronous JavaScript at Netflix

Netflix: Observable EverywhereApp StartupPlayerData AccessAnimationsView/Model binding

Page 55: Asynchronous JavaScript at Netflix

Resources https://github.com/Reactive-Extensions/RxJS RxJava http://jhusain.github.io/learnrx/

Page 56: Asynchronous JavaScript at Netflix

Questions