Understanding Asynchronous JavaScript

1,764
-1

Published on

An easy dive into asynchronous programming and how it works with JavaScript's event loop, queue, and multiple threads.

Published in: Internet, Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,764
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
86
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

Understanding Asynchronous JavaScript

  1. 1. John Newman @jnewmanux • github.com/jgnewman rescuecreative@gmail.com UNDERSTANDING ASYNCHRONOUS JAVASCRIPT TallyJS - June 2014
  2. 2. What is asynchronous programming?
  3. 3. “There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors. - Unknown Somebody go to Google and search "define synchronous"
  4. 4. Wikipedia codewala.net Our Simple Definition Parallel computing is a form of computation in which many calculations are carried out simultaneously, operating on the principle that large problems can often be divided into smaller ones, which are then solved concurrently ("in parallel"). Asynchronous operation means that a process operates independently of other processes. If there are multiple operations, [they] can be handled [using] different processes/threads without waiting to complete the other ones. When the flow of your program is such that code might be executed out of order or you might not be able to count on one thing happening before another thing.
  5. 5. But why does (JavaScript) code run in order in the first place?
  6. 6. // Define some functions JS function first() { return 'hello'; } function second() { return first(); } function third() { first(); return second(); } function fourth() { first(); second(); return third(); } // Execute the program 01 first(); 02 second(); 03 third(); 04 fourth(); Code is executed in a "single thread."
  7. 7. // Define some functions JS 01 function last() { 02 console.log("from L.A. to Tokyo"); 03 } 04 function doProgram() { 05 setTimeout(last, 0); 06 console.log("i'm so fancy"); 07 console.log("you already know"); 08 console.log("i'm in the fast lane"); 09 } // Execute the program 10 doProgram(); ! //=> i'm so fancy //=> you already know //=> i'm in the fast lane //=> from L.A. to Tokyo
  8. 8. “ When there is nothing to do, Check the queue. But only check the queue When there's nothing left to do. - JavaScript In other words, the current flow will NEVER be interrupted by code existing in the queue.
  9. 9. // Add a click handler to the document. document.addEventListener('click', function () { console.log('click happened!'); }); ! // Create a function that does nothing but waste time. function wasteTime() { var i; for (i = 0; i < 3000000; i += 1) { new Array(10000000); } return 'finished wasting time'; } ! // Start wasting time. wasteTime(); JS //=> finished wasting time //=> click happened! Let's prove it in the console!
  10. 10. JS Some similar examples that use the queue: [ELEMENT].addEventListener('click', myFunction); ! [ELEMENT].onclick = myFunction; ! $([ELEMENT]).click(myFunction); ! <a onclick="myFunction">click me</a> The queue matters because any time your program receives input, JavaScript puts the input handler into the queue.
  11. 11. So when we talk about "asynchronous JavaScript," we're really just talking about any code that puts things in the queue.
  12. 12. And if we can't know when code will enter the queue (for example, application input), we can't know when it will be executed.
  13. 13. 1 2 3 Ways to Receive Input DOM events such as clicks, hovers, keyups, etc. ! ! Handlers go in the queue. HTTP(s)/websocket communication events. (Includes $.ajax) ! ! Handlers go in the queue. Web worker message passing. (How we do threads in the browser) ! ! Handlers go in the queue.
  14. 14. And if it's in the queue, you can't return it or error handle it.
  15. 15. function getGithubInfo() { var ajax = $.ajax({ url: 'https://api.github.com/users/jgnewman/repos', dataType: 'jsonp' }); return ajax; } ! getGithubInfo(); JSLet's access some real data:
  16. 16. function getGithubInfo() { var ajax = $.ajax({ url: 'https://api.github.com/users/jgnewman/repos', dataType: 'jsonp' }); return ajax; } ! getGithubInfo(); JSLet's access some real data: NOPE! But clearly that's because $.ajax is returning the jQuery request object instead of the actual data. Let's fix that.
  17. 17. function getGithubInfo() { var ajax = $.ajax({ url: 'https://api.github.com/users/jgnewman/repos', dataType: 'jsonp' }); return ajax.done(function (data) { return data; }); } ! getGithubInfo(); JSLet's access some real data:
  18. 18. function getGithubInfo() { var ajax = $.ajax({ url: 'https://api.github.com/users/jgnewman/repos', dataType: 'jsonp' }); return ajax.done(function (data) { return data; }); } ! getGithubInfo(); JSLet's access some real data: STILL NOPE! Right, but this time it's because .done is a Promise method so it's returning another Promise object. I got it this time…
  19. 19. function getGithubInfo() { var result; $.ajax({ url: 'https://api.github.com/users/jgnewman/repos', dataType: 'jsonp' }).done(function (data) { result = data; }); return result; } ! getGithubInfo(); JSLet's access some real data:
  20. 20. function getGithubInfo() { var result; $.ajax({ url: 'https://api.github.com/users/jgnewman/repos', dataType: 'jsonp' }).done(function (data) { result = data; }); return result; } ! getGithubInfo(); JSLet's access some real data: FRACK! STILL NOPE! Because no matter what we do, ajax requires an event handler and event handlers go into the queue. Which means…
  21. 21. It gets executed AFTER the function returns.
  22. 22. function logResult(result) { console.log(result); } ! function getGithubInfo() { var ajax = $.ajax({ url: 'https://api.github.com/users/jgnewman/repos', dataType: 'jsonp' }); ajax.done(logResult); } ! getGithubInfo(); JSThis time let's really do it:
  23. 23. function logResult(result) { console.log(result); } ! function getGithubInfo() { var ajax = $.ajax({ url: 'https://api.github.com/users/jgnewman/repos', dataType: 'jsonp' }); ajax.done(logResult); } ! getGithubInfo(); JSThis time let's really do it: SUCCESS!
  24. 24. What was that you said about error handling?
  25. 25. function logResult(result) { console.log(result); } ! function getGithubInfo() { var ajax; try { ajax = $.ajax({ url: 'fhqwhgads', dataType: 'jsonp' }); ajax.done(logResult); } catch (err) { console.log('got an error', err); } } ! getGithubInfo(); JSNo error handling allowed:
  26. 26. function logResult(result) { console.log(result); } ! function getGithubInfo() { var ajax = $.ajax({ url: 'fhqwhgads', dataType: 'jsonp' }); ajax.done(logResult); ajax.fail(function (errData) { console.log('got an error', errData); }); } ! getGithubInfo(); JSThe jQuery Solution:
  27. 27. Asynchrony can be hard to reason about when it gets complex. So let's talk about some common techniques that can make it feel more synchronous.
  28. 28. CALLBACK FUNCTIONS PROMISE OBJECTS
  29. 29. // Import the file system library var fileSystem = require('fs'); ! // Call `readdir` (which is asynchronous) to get a list // of files in the directory fileSystem.readdir('/myfiles', function (err, data) { if (err) { handleTheError(err); } else { doSomethingWith(data); } }); JSWhat Everyone Hates About Node.js What's wrong with this pattern, you ask?
  30. 30. /** * Show a list of songs for the artist with the given name and * execute the callback when complete. */ function showSongs(artistName, callback) { // Notice we're not even thinking about errors here artists.getByName(artistName, function (artist) { albums.getByArtist(artist, function (albums) { songs.getByAlbum(albums, function (songs) { songView.render(songs); return callback(); }); }); }); } JSThe fact that it gets nasty really fast: example stolen from clutchski on slideshare
  31. 31. Can Promises fix that problem? The can definitely ice that cake.
  32. 32. What is a Promise? ‣ It's an object with methods (usually done, then, and fail). ‣ Each method takes at least 1 callback. ‣ The callbacks are executed under different conditions (done or then on success, fail on error ). ‣ Each Promise method returns another promise. ‣ Therefore, you can chain asynchronous actions. ‣ And you can attach as many callbacks as you want to a given event.
  33. 33. function myAsyncFn() { return new Promise(function (resolve, reject) { var async = somethingAsynchronous(); async.addEventListener('success', resolve()); async.addEventListener('error', reject()); }); } JSFor example: var myPromise = myAsyncFn(); myPromise.done(function () { …success stuff… }); myPromise.done(function () { …more success stuff… }); myPromise.fail(function () { …failure stuff… }); myPromise.fail(function () { …more failure stuff… }); myPromise.then(function () { return myAsyncFn() }) .then(function () { return myAsyncFn() }) .then(function () { return myAsyncFn() });
  34. 34. So let's go back to our ugly, nested callbacks.
  35. 35. /** * Show a list of songs for the artist with the given name and * execute the callback when complete. */ function showSongs(artistName, callback) { artists.getByName(artistName, function (artist) { albums.getByArtist(artist, function (albums) { songs.getByAlbum(albums, function (songs) { songView.render(songs); return callback(); }); }); }); } JSPreviously we had this:
  36. 36. /** * Show a list of songs for the artist with the given name and * execute the callback when complete. */ function showSongs(artistName, callback) { artists.getByName(artistName) .then(function (artist) { return albums.getByArtist(artist) }) .then(function (albums) { return songs.getByAlbum(albums) }) .then(function (songs) { return songView.render(songs) }) .then(callback); } JSBut if we used promises instead… Promises are going to be native in ES6! (Currently 0 IE support)
  37. 37. jQuery Tons of Others Q for Node.js http://api.jquery.com/promise/ http://promisesaplus.com/implementations https://github.com/kriskowal/q
  38. 38. At this point, you may be wondering… ‣ Why asynchrony is always talked about in terms of concurrency when the word itself seems to imply the exact opposite ‣ What asynchronous JavaScript looks like using web workers ‣ When this talk is going to end (Check the time, future John)
  39. 39. Concurrency becomes asynchrony when parallel threads talk to each other.
  40. 40. Evaluate main thread and worker thread JS Queue Queue Main thread sends message to worker which goes into the worker's queue Worker sends message to main thread which goes into main thread's queue Each thread can be run on its own core thus making multiple actions actually simultaneous. Every time a message is sent, that message goes into a queue thus making the code within each thread independently asynchronous.
  41. 41. A web worker is… ‣ A way to spin up multiple threads in browser-based JavaScript ‣ Theoretically, an extremely helpful tool that has been much needed for quite some time ‣ In reality, a fairly annoying and cumbersome implementation of a concept that ought to have been much simpler
  42. 42. JS var worker = new Worker('myworker.js'); ! worker.onmessage = function (evt) { console.log('The worker sent', evt.data); }; ! worker.postMessage(''); JS self.onmessage = function () { self.postMessage('message received'); }; main.js myworker.js
  43. 43. // Takes a function and turns it into a web worker function createWorker(fn) { var body = fn.toString().replace(/^.+{|}s*$/g, ''), blob = new Blob([body], {type: 'text/javascript'}); return new Worker(window.URL.createObjectURL(blob)); } JS There's also a "secret" way to do it with one file: // The intended functionality of our web worker function workerBody() { self.onmessage = function () { self.postMessage('message received'); }; } // Create and use the worker var worker = createWorker(workerBody); worker.onmessage = function (evt) { console.log('The worker sent', evt.data); }; worker.postMessage('');
  44. 44. JS Let's use it to make life a bit easier: // The minified worker maker function createWorker(f){var b=f.toString().replace(/^.+{|}s*$/g,''),l=new Blob([b],{type:'text/javascript'});return new Worker(window.URL.createObjectURL(l))} ! // A worker body containing a modified version of our time wasting function from before. // Creates 3 million arrays with 10 million items and returns the amount of // time that took. function workerBody() { function wasteTime() { var i, begin = Date.now(); for (i = 0; i < 3000000; i += 1) { new Array(10000000); } return Date.now() - begin; } self.onmessage = function () { var time = wasteTime(); self.postMessage('Wasted ' + time + 'ms of time.'); }; } ! // Set up a click handler on the document var clicks = 0; document.addEventListener('click', function () { console.log('click', ++clicks, 'happened') }); ! // Launch the worker and try it out. var worker = createWorker(workerBody); worker.onmessage = function (evt) { console.log(evt.data) }; worker.postMessage();
  45. 45. Check out where I actually use this concept…
  46. 46. STREAMPUNK ! A simple & surprisingly powerful application framework that offers data binding, single page apps, and tons of other tools. ! streampunkjs.com SOLID FUEL SOCKET BOOSTER ! A library for the browser that allows you to take common ajax/websocket actions and dynamically run them within a web worker. ! github.com/jgnewman/sfsb
  47. 47. You can also check out htmlgoodies.com for more examples of Web Workers in action. http://www.htmlgoodies.com/html5/client/introduction- to-html5-web-workers-use-cases-and-identify-hot- spots-.html
  48. 48. THANK YOU! John Newman @jnewmanux rescuecreative@gmail.com
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×