Asynchronous java script

51
ASYNCHRONOUS JAVASCRIPT GET STUFF DONE BY KNOWING WHEN STUFF IS DONE.

description

A presentation I gave about asynchronous JavaScript strategies

Transcript of Asynchronous java script

Page 1: Asynchronous java script

ASYNCHRONOUSJAVASCRIPT

GET STUFF DONE BY KNOWING WHEN STUFF IS DONE.

Page 2: Asynchronous java script

WANT TO HEAR A JAVASCRIPT JOKE?

CALLBACK LATER ANDI'LL TELL IT TO YA!

Page 3: Asynchronous java script

CALLBACKS ARE HOW JAVASCRIPT MANAGES THINGS THATNEED TO HAPPEN IN THE FUTURE

function deliverPunchline() { console.log("Because they take things literally.");}

function setUpJoke() { console.log("Why don't kleptomaniacs like puns?"); setTimeout(deliverPunchline, 2000);}

setUpJoke();

Page 4: Asynchronous java script

EVENT PUMPsetUpJoke

deliverPunchline (after 2000 ms)

EXECUTION THREADsetUpJoke

deliverPunchline

Page 5: Asynchronous java script

WHAT HAPPENS IF THE EXECUTION THREAD IS BUSY?function deliverPunchline() { console.log("Because seven has some badass tattoos.");}function contrivedExample() { console.log("Why is six afraid of seven?"); setTimeout(deliverPunchline, 2000);

findPrimeFactorsFor(EXTREMELY_LARGE_NUMBER);}

function contrivedExample();

Page 6: Asynchronous java script

EVENT PUMPcontrivedExample

deliverPunchline in 2000 ms (2000 ms are up!)

EXECUTION THREADcontrivedExample (still finding factors)deliverPunchline WAY after 2000 ms!

Page 7: Asynchronous java script

IN JAVASCRIPT, EVENTSARE BLOCKED BY

CURRENTLY EXECUTINGCODE.

Page 8: Asynchronous java script

"In JavaScript, events are blocked by currentlyexecuting code." -RonTime

Page 9: Asynchronous java script

"

" -Your Name Here

"In JavaScript, events are blockedby currently executing code." -

RonTime

Page 10: Asynchronous java script

EVENT PUMPuser furiously smashing keyboard event

scroll eventmouse click event

deliverPunchline in 100 ms

EXECUTION QUEUEcontrivedExample (still finding factors)

Page 11: Asynchronous java script

THAT'S COOL, BUT I DON'T USE SETTIMEOUT OR WRITEFUNCTIONS THAT DO HEAVY COMPUTATION.

Page 12: Asynchronous java script

A SEQUENCE OF USER EVENTS ARE ASYNCHRONOUS BYNATURE.

Page 13: Asynchronous java script

But more importantly...

THE FIRST 'A' IN 'AJAX'IS 'ASYNCHRONOUS'

Page 14: Asynchronous java script

(The 'X' in 'Ajax' is 'XML', but we'll ignore that)

Page 15: Asynchronous java script

function loadStuff() { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if(xhr.readyState === 4) { console.log(xhr.responseText); } } xhr.open('GET', '/doit?delay=5'); xhr.send();

console.log("Request sent!");}

loadStuff();

Page 16: Asynchronous java script

YOU CAN'T WRITE ASYNCHRONOUS CODE SYNCHRONOUSLYfunction getStuff() { var xhr = new XMLHttpRequest(); var results; xhr.onreadystatechange = function() { if(xhr.readyState === 4) { results = xhr.responseText; } } xhr.open('GET', '/doit?delay=5'); xhr.send(); return results;}

console.log("Stuff is ", getStuff());

Page 17: Asynchronous java script

YOU CAN MAKE SOME ASYNCHRONOUS CODE SYNCHRONOUS,BUT EXECUTION WILL BLOCK ALL EVENTS

function getStuff() { var xhr = new XMLHttpRequest(); var results; xhr.onreadystatechange = function() { if(xhr.readyState === 4) { results = xhr.responseText; } } xhr.open('GET', '/doit?delay=5', false); // third param is the 'async' param xhr.send(); return results;}

console.log("Stuff is ", getStuff());

Page 18: Asynchronous java script

HOW DO WE MAKE THE ASYNCHRONOUS CODE MOREUNDERSTANDABLE?

CALLBACKS

Page 19: Asynchronous java script

function getStuff(url, callback) { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if(xhr.readyState === 4) { callback(xhr.responseText); } } xhr.open('GET', url); xhr.send();}

getStuff('/doit?delay=5', function(response) { console.log(response);});

Page 20: Asynchronous java script

BUT WHAT IF I NEED TO DO THINGS THAT DEPEND ON EACHOTHER?

Page 21: Asynchronous java script

getStuff('/user/current', function(result) { var userName = getNameFromResult(result); console.log("Hello, " + userName); var messageURI = getMessageURIFromResult(result); getStuff(messageURI, function(messages) { var messageCount = getMessageCountFromResult(messages); console.log( "You have " + messageCount + " messages."); });});

Page 22: Asynchronous java script

WHAT ABOUT (*GASP*) ERRORS?

Page 23: Asynchronous java script

getStuff('/user/current', function(result) { var userName = getNameFromResult(result); console.log("Hello, " + userName); var messageURI = getMessageURIFromResult(result); getStuff(messageURI, function(messages) { var messageCount = getMessageCountFromResult(messages); console.log( "You have " + messageCount + " messages."); }, function(err) { console.log( "An error occurred getting messages:", err.message); });}, function(err) { console.log( "An error occurred getting user data:", err.message);});

Page 24: Asynchronous java script

getStuff('/user/current', function(xhr, err) { if(err) { console.log( "An error occurred getting user data:", err.message); } var userName = getNameFromResult(results); console.log("Hello, " + userName); var messageURI = getMessageURIFromResult(results); getStuff(messageURI, function(messages, err) { if(err) { console.log( "An error occurred getting messages:", err.message); } var messageCount = getMessageCountFromResult(messages); console.log( "You have " + messageCount + " messages."); });});

Page 25: Asynchronous java script

GOOD THINGS WITH THIS APPROACH?UnderstandableEasy to consumeDirect API

Page 26: Asynchronous java script

PROBLEMS WITH THIS APPROACH?ReadabilityTestingTight coupling / Wrong direction of dependenciesPyramid of doom

Page 27: Asynchronous java script

HOW CAN WE ADDRESS THESE PROBLEMS?

Page 28: Asynchronous java script

CAN WE MANAGE ASYNCHRONOUS PROCESSES BY SHARINGAN OBJECT BETWEEN THE CONSUMER AND THE PRODUCER?

Page 29: Asynchronous java script

Consumer -> Shared Object <- Producer

Page 30: Asynchronous java script

function getStuffAsSharedObject(url) { var xhr = new XMLHttpRequest(); var shared = { results: null }; xhr.onreadystatechange = function() { if(xhr.readyState === 4) { shared.results = xhr.responseText; } } xhr.open('GET', url); xhr.send(); return shared;}

function createSharedHandler(shared) { var handler = function() { if(!shared.results) { setTimeout(handler, 0); } else { console.log(shared.results); } } return handler;}

var shared = getStuffAsSharedObject('doit?delay=5');createSharedHandler(shared)();

Page 31: Asynchronous java script

Create an object in a producer that can be told that processingis doneGet an object from the producer to the consumer so that theconsumer can tell it to do something

Page 32: Asynchronous java script

function getStuffWithCoordinator(url) { var xhr = new XMLHttpRequest(); var coordinator = new Coordinator(); xhr.onreadystatechange = function() { if(xhr.readyState === 4) { coordinator.allDoneAndTheDataIs(xhr.responseText); } } xhr.open('GET', url); xhr.send(); return coordinator.consumerObject();}

Page 33: Asynchronous java script

getStuffWithCoordinator('/user/current').andThen(function(result) { var userName = getNameFromResult(result); console.log("Hello, " + userName); var messageURI = getMessageURIFromResult(result);

return getStuffWithCoordinator(messageURI);}).andThen(function(result) { var messageCount = getMessageCountFromResult(result); console.log("You have " + messageCount + " messages.");});

Page 34: Asynchronous java script

function Coordinator() { var callbacks = []; var exceptionHandlers = []; return { allDoneAndTheDataIs: function(data) { for(var i = 0; i < callbacks.length; i++) { callbacks[i](data); } }, thingsScrewedUp: function(ex) { for(var i = 0; i < callbacks.length; i++) { exceptionHandlers[i](ex); } }, consumerObject: function() { return { andThen: function(fn, err) { var coordinator = Coordinator(); var callback = function() { result.andThen(coordinator.allDoneAndTheDataIs, coordinator.thingsScrewedUp); callbacks.push(callback); return coordinator.consumerObject(); } } } }}

Page 35: Asynchronous java script

PROMISE

Page 36: Asynchronous java script

A that builds out a standard for asynchronouscommunication using a shared communication object called a

promise.

PROMISES/A+spec

Page 37: Asynchronous java script

SO YOU TURNED NESTED CALLBACKS INTO A CHAIN.

BIG WHOOP!

Page 38: Asynchronous java script

A promise represents the future value that will be returned.

Page 39: Asynchronous java script

function getStuffAsPromise(url) { var xhr = new XMLHttpRequest(); var deferred = Q.defer(); xhr.onreadystatechange = function() { if(xhr.readyState === 4) { if(xhr.statusCode === 500) { throw "Something pooped on the server!"; } else { deferred.resolve(JSON.parse(xhr.responseText)); } } } xhr.open('GET', url); xhr.send(); return deferred.promise;}

Page 40: Asynchronous java script

function getUserInfoAsPromise() { return getStuffAsPromise('/user/current') .then( function(data) { console.log( "Hello, ", data.name); return data; });}

Page 41: Asynchronous java script

function getMessagesAsPromise(data) { return getStuffAsPromise(data.messageURI).then(function(data) { console.log( "You have", data.messages.length, "messages."); return data; });}

Page 42: Asynchronous java script

Q.fcall(getUserInfoAsPromise).then(getMessagesAsPromise);

Page 43: Asynchronous java script

EXCEPTION HANDLINGfunction getError() { throw "Oh shit!";}

Q.fcall(getError()).then(getUserInfoAsPromise).then(getMessagesAsPromise).catch(function(err) { console.log(err);});

Page 44: Asynchronous java script

COMPOSITIONfunction mapPromises(promises, fn) { return Q.all(promises).then(function(results) { var mappedResults = []; for(var i = 0; i < results.length; i++) { mappedResults.push(fn(results[i])); } return mappedResults; });}

function sortPromises(promises) { function getData(result) { return parseInt(result.data, 10); }

return mapPromises(promises, getData).then(function(results) { return results.sort(); });}

function setUp() { var data = []; for(var i = 0; i < 10; i++) { data.push(getStuffAsPromise( 'doit?x=' + i)); }

sortPromises(data).then(function(sorted) { for(var i = 0; i < sorted.length; i++) { console.log(sorted[i]); } });

Page 45: Asynchronous java script

Promises are more than just a fancy callback system. A promisestands in place of a future value.

Page 46: Asynchronous java script

When an object is "thenable", the value can be retrieved in thefuture and used directly, making asynchronous concepts look

synchronous.

Page 47: Asynchronous java script

GOOD THINGS WITH THIS APPROACHCode is much more flatEasier to testWe can compose functions that take promisesWe can handle exceptions very clearlyWe can pass around promises as though they were the actualvalue, using .then() to get the result

Page 48: Asynchronous java script

BAD THINGS WITH THIS APPROACHTends to cause some confusion because the abstraction isn'tstraightforwardUsing this in a public API is considered very opinionated, andforces consumers of your API to use a specific paradigmMultiple implementations of this, not all meet the standard(*cough* jQuery *cough*)

Page 49: Asynchronous java script

RECAP

Page 50: Asynchronous java script

Asynchronous processes in JavaScript are handled withcallbacks.Callback mechanisms introduce some problems such as errorhandling, composition, testing.Callbacks are functions, which are first class objects.Instead of callbacks, what if we used a different object thatmediates between consumer and producer?Promise objects are a type of mediator that represent thefuture value of an async process.Promises/A+ is an open standard with a specification.Promise objects can be used in place of actual values to makeasynchronous code look synchronous.

Page 51: Asynchronous java script

QUESTIONS?