Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Developing Async Sense

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Developing Async Sense

This talk will take a deep dive into core asynchronous patterns in JavaScript, comparing the tradeoffs of callbacks, promises and async/await and demonstrating how each pattern builds on top of the previous, improving our async handling capabilities.

The goal is to show how we can use these patterns to write cleaner and more reliable (concurrent) code and demystify concepts such as the Event Queue, Single-threaded, Run to Completion and Non-Blocking.

We'll learn how to deal with things happening “at the same time “ and even eliminate time as a concern.

Avatar for Nemanja Stojanovic

Nemanja Stojanovic

March 17, 2019
Tweet

More Decks by Nemanja Stojanovic

Other Decks in Programming

Transcript

  1. Concurrent vs Parallel Concurrency: Time-independent Composition Structure code into time-independent

    (interruptible) chunks Parallelism: Time-independent Execution Logical/ Structural Physical/ Executional Execute code fully independent of another code
 (*including a separate simultaneous execution of the same code)
  2. Concurrent vs Parallel (2 tasks example) 1. Call ATT to

    cancel your subscription 2. Write a blog post Concurrent: you call ATT and work on your blog while on hold Parallel: your roommate calls ATT for you while you work on the blog post
  3. Sync vs Async Concepts based on time, not independency or

    interuptability Sync code: runs during the current execution Async code: runs at some point after current execution
  4. Async vs Sync example 1. Call ATT to cancel your

    subscription Sync: call ATT and wait on hold for the operator Async: call ATT and schedule a call back from them when the operator is available
  5. Sync means things must happen chronologically one after the other

    Async means things can happen chronologically independent of one another Task 1 ✅ Time Task 2 ✅ Task 3 ✅ Task 1 ✅ Time Task 2 ✅ Task 3 ✅ Sync vs Async
  6. Treads Interruptible execution of a piece of code We can

    execute any piece of code, including the same one, multiple-times concurrently. We can pause, perform other tasks and continue the execution from where we left off
  7. Problem with Threads - Data Corruption Risks Shared Memory ->

    Races who gets to the shared data first
  8. arr.length arr.push('b'); arr.length arr.push('a'); arr.length arr.length arr.push('a'); arr.push('a'); if (arr.length

    == 1) { arr.push('b'); } arr.push('a'); arr.push('a'); if (arr.length == 1) { arr.push('b'); } arr.push('a'); arr.push('b'); arr.length arr.push('a'); Possibility 1 Possibility 2 [ ] [ ] 'a' 'a', 'b', 'a', 'a' arr.push('a'); if (arr.length == 1) { arr.push('b'); } arr.length arr.push('a'); arr.push('a'); if (arr.length == 1) { arr.push('b'); } Possibility 3 [ ] 'a', 'a', 'b' Thread 1 Thread 2 Thread 1 Thread 2 Thread 1 Thread 2
  9. Tread-safe code Mutual Exclusion (Mutex) - Semaphores, monitors, synchronization Mutex

    is a program object that prevents simultaneous access to a shared resource => critical section) - Each critical section is executed once per thread
  10. lock(); // lock execution to the current thread arr.push('a'); if

    (arr.length == 1) { arr.push('b'); } unlock(); // free execution for the next thread Only possible output: [ ] 'a' 'a', 'b', Tread-safe code
  11. JavaScript Although the JS Engine might have multiple threads for

    different things (Events, Rendering, Network…) the results get to JS land one at a time Single Threaded
  12. JavaScript - Single Threaded Only one thing can happen at

    a time (single Call Stack)* *workers are an exception While JS code is running the UI is blocked
  13. JavaScript Code Run-to-Completion semantic Function code is atomic (once a

    function starts it has to finish*) That’s why if the CSS Engine wants to repaint but JS is still running we get jerkiness *generators and async/await
 are the exception
  14. function one() { } console.log('A'); function two() { console.log('B'); }

    console.log('C'); Console console.log('A'); 'A' console.log('C'); 'C' 'B' Call Stack log('A') log('C') console.log('B'); log('B') two(); two(); one(); one(); one() two()
  15. JavaScript - Single Threaded Only one thing can happen at

    a time* Async code runs after the Call Stack is empty Async code doesn’t block the UI because its processed later *workers are an exception
  16. Why does this matter? Analogy:
 Because being on hold sucks.

    Imagine having the customer service call you when they’re available. Much better, right? Good UX === Non-Blocking Who cares?
  17. Callbacks A callback function is a function which is: -

    passed as an argument* to another function, and, - is invoked after some kind of event. *Because JS functions are first-class functions
  18. Callbacks aren’t async Have no concept of asynchronicity Using callbacks

    in a sync manner is, in fact, common, for example in the Array.prototype.forEach method [1, 2, 3].forEach(() => { // this code runs synchronously }) // this line isn’t reached until // the iteration above is done
  19. var one = getOneSync(); getThreeAsync((three) => { alert(three); }); var

    two = getTwoSync(); var one = getOneSync(); getThreeAsync((three) => { alert(three); }); var two = getTwoSync(); var one = getOneSync(); getThreeAsync((three) => { alert(three); }); var two = getTwoSync(); NOW LATER
  20. Event Loop Infinitely looping looking for new tasks to execute.

    A single iteration is called a tick. The code executed during a tick is called a task. Runs on a single thread 
 => Cannot execute a task while another task is running
  21. Event Loop Tasks are synchronous Tasks may schedule other tasks

    in the loop. Tasks may come from other sources such as user events, 
 networking or DOM manipulation.
  22. function eventLoop() { // perform loop ticks, "forever" while (true)

    { var nextTask = taskQueue[0]; nextTask(); } }
  23. console.log('A'); setTimeout(function cb() { console.log('B'); }, 1000); console.log('C'); Console Task

    Queue console.log('A'); 'A' console.log('C'); cb() 'C' 'B' setTimeout(function cb() { console.log('B'); }, 1000); Browser Call Stack log('A') setTimeout() log('C') Wait 1sec console.log('B'); log('B')
  24. console.log('A'); setTimeout(function cb() { console.log('B'); }, 0); console.log('C'); Console Task

    Queue console.log('A'); 'A' console.log('C'); cb() 'C' 'B' setTimeout(function cb() { console.log('B'); }, 0); Browser Call Stack log('A') setTimeout() log('C') Wait .004sec console.log('B'); log('B') Wait until 
 Stack is Empty
  25. Why is setTimeout(cb, 0) useful? setTimeout(cb, 0) Defer code execution

    until the Call Stack is empty Add heavy-weight code to the end of the Event Loop Allow the browser to re-render and/or process events 
 before executing our code
  26. Example function process(data) { var chunk = data.slice(0, 10); //

    take first 10 items //... do heavy-weight stuff with the chunk // stick this function at the end of the event queue setTimeout(() => { // continue processing the data after Call Stack is clear process(data.slice(10)) }, 0); }
  27. Callback Hell httpGet('/stories/' + storyId, function (story) { httpGet(story.chapterUrls[0], function

    (chapter) { httpGet(chapter.pageUrls[0], function (page) { var chapterJSON = JSON.parse(chapter); displayChapter(chapterJSON); }); }); Most known callback issue - Pyramid of doom
  28. Pyramid of Doom - Solved? httpGet('/stories/' + storyId, processStory); 


    function processStory(story) { httpGet(story.chapterUrls[0], processChapter); } function processChapter(chapter) { httpGet(chapter.pageUrls[0], processPage); } function processPage(page) { var chapterJSON = JSON.parse(chapter); displayChapter(chapterJSON); }
  29. Turns out… there’s a lot more Execution order is difficult

    to maintain Errors are hard to handle (flow branches on errors) - Require manual error handling (no try/catch) Non-Sequential Flow } - Require maintaining state in the global scope Mixing input and output - Couple async operation with our reaction to it
  30. And some more… Being called too many times Swallowing errors

    or exceptions Unreliable Execution } No built-in protection against: Being called too early
  31. Non-Sequential Flow Callbacks - Getting two values in parallel? httpGet(url1,

    function(res) { // here we have value 1 }); httpGet(url2, function(res) { // here we have value 2 }); function processValues(value1, value2) { // how do we know when we have both? }
  32. function(res) { value1 = res; if (typeof value2 !== 'undefined')

    { processValues(value1, value2); } var value1, value2; var value1, value2; httpGet(url1, function(res) { value1 = res; if (typeof value2 !== 'undefined') { processValues(value1, value2); } }); httpGet(url2, function(res) { value2 = res; if (typeof value1 !== 'undefined') { processValues(value1, value2); } }); processValues(value1, value2); processValues(value1, value2); function(res) { value2 = res; if (typeof value1 !== 'undefined') { processValues(value1, value2); } Maintain state externally Mixing input and output Repetition (not DRY) Non-Sequential Flow
  33. processValues(values.value1, values.value2); var values = { value1: undefined, value2: undefined

    }; var hasValues = { value1: false, value2: false }; httpGet(url1, getRequestHandler('value1')); httpGet(url2, getRequestHandler('value2')); function getRequestHandler(valueNum) { return function(res) { hasValues[valueNum] = true; values[valueNum] = res; if (hasValues.value1 && hasValues.value2) { processValues(values.value1, values.value2); } } } function (res) { hasValues[valueNum] = true; values[valueNum] = res; if (hasValues.value1 && hasValues.value2) { processValues(values.value1, values.value2); } } getRequestHandler('value2') getRequestHandler('value1') var values = { value1: undefined, value2: undefined }; var hasValues = { value1: false, value2: false }; Maintain state externally Mixing input and output DRY Non-Sequential Flow
  34. var values = {}; var hasValues = {}; processValues.apply(
 null,

    getAllValues()
 ); var values = {}; var hasValues = {}; urls.forEach(function(url) { httpGet(url, getRequestHandler(url)); }); function getRequestHandler(url) { return function(res) { hasValues[url] = true; values[url] = res; if (hasAllValues()) { processValues.apply(
 null, getAllValues()
 ); } } } function hasAllValues() { return urls.every(function(url) { return hasValues[url]; }); } function getAllValues() { return urls.map(function(url) { return values[url]; }); } DRY Non-Sequential Flow Maintain state externally Mixing input and output Generalized
  35. What if we wanted to make parallel requests but process

    data as soon as it’s received and in a particular order?
  36. Let’s say we want to request value1 and value2 at

    the same time but process them in order (value1 then value2)
  37. processValue(value2); value2 = result; processValue(value1); value1 = result; value1 received

    first 1. receive value1 2. process value1 3. receive value2 4. process value2 var value1, value2; httpGet(url1, function(res) { value1 = result; processValue(value1); if (typeof value2 !== 'undefined') { processValue(value2); } }); httpGet(url2, function(res) { value2 = result; if (typeof value1 !== 'undefined') { processValue(value2); } });
  38. processValue(value2); var value1, value2; httpGet(url1, function(res) { value1 = result;

    processValue(value1); if (typeof value2 !== 'undefined') { processValue(value2); } }); httpGet(url2, function(res) { value2 = result; if (typeof value1 !== 'undefined') { processValue(value2); } }); value2 = result; processValue(value1); value1 = result; value2 received first 1. receive value2 2. receive value1 3. process value1 4. process value2
  39. var values = {}; var processed = {}; processValue(values[url]); var

    values = {}; var processed = {}; urls.forEach(function(url) { httpGet(url, function(res) { values[url] = res; urls.every(function(url) { if(values[url]) { if (!processed[url]) { processValue(values[url]); processed[url] = true; } } return !!processed[url]; }); }); }); Non-Sequential Flow Maintain state externally Mixing input and output DRY Generalized
  40. sendPurchaseOrder(books[0].url, function() { }); getBooks(store.booksUrl, function(books) { ; }); Unreliable

    getStore(url, function (store) { }); displaySuccessDialog(); Can we guarantee that getStore won’t call its callback twice which would call getBooks twice and then send two purchase orders? How about if getBooks sometimes returns an invalid url array?
  41. var hasExecuted = false; processValue(res); var hasExecuted = false; httpGet(url,

    function(res) { if (!hasExecuted) { hasExecuted = true; processValue(res); } }); A callback that runs once Non-Sequential Flow Maintain state externally Mixing input and output
  42. We could build a safety-mechanism We must remember to use

    it A lot more code to maintain httpGet(url, once(processValue)); function once(cb) { var hasExecuted = false; return function(...args) { if (!hasExecuted) { hasExecuted = true; cb(...args); } });
  43. Error Handling Separate handlers for success and fail cases $.ajax({

    success: function() { // ... }, error: function() { // ... } })
  44. $.ajax({ success: function() { }, error: function() { 
 }

    }) - Reduces (somewhat) the issue of
 Non-Sequential Flow by splitting logic // success path // ... // ... // error path
  45. $.ajax({ success: function() { }, error: function() { } })

    Doubles our need for protection
 against Unreliable Execution because now we have two callbacks to worry about instead of one I am unreliable Me too! // Not called on time? // Not called at all?
 // Called to many times? // Not called on time? // Not called at all?
 // Called to many times?
  46. What if res is an invalid JSON? Bloat code with

    noise by requiring error handling logic in every callback Error Handling function getJSON(url, cb) { httpGet(url, function(err, res) { if (err) { cb(err); } else { cb(null, JSON.parse(res)) } }); } Error-first style callbacks
  47. What if cb() throws an error? function cb(err, json) {

    throw Error('Error within cb()') } function getJSON(url, cb) { httpGet(url, function(err, res) { if (err) return cb(err); try { cb(null, JSON.parse(res)); } catch(err) { cb(err); } }); } Error Handling Error-first style callbacks
  48. …but that code is complex, and usually has to be

    repeated for each callback If only we had a built in way to handle all of this…. Ok so, if we wanted to, we could solve most of the callback problems with a bunch of extra code
  49. Promises Eventual result of an asynchronous operation Wrappers around future

    values Inherently Reliable Eliminate time as a concern - We can imagine as if we already have the result, when it arrives is irrelevant. Handle errors
  50. Promises var promise = new Promise(function(resolve, reject) { // we

    call resolve() on success // we call reject() on fail }); How do we create a Promise? promise.then(function(value) { // here we have the value }, function(error) { // here we have the error }); How do we use a Promise?
  51. Promises var listener = new Promise(function(resolve, reject) { // we

    call resolve() on success // we call reject() on fail }); We can imagine Promise returning a listener listener.on('success', function(value) { // here we have the value }); listener.on('error', function(error) { // here we have the error });
  52. Promises var promise = new Promise(function(resolve, reject) { // we

    call resolve() on success // we call reject() on fail }); promise.then(function(value) { // here we have the value }, function(error) { // here we have the error }); - Creating a Promise returns immediately - And we can use that promise anytime and anywhere later to resolve it to a value Do not mix input and output
  53. Promises console.log(1); 
 var promise = new Promise(function(resolve, reject) {


    console.log(2); // this gets logged right after 1 resolve(); // only this part is async }).then(function () { console.log(4); }); console.log(3); - The code within the Promise constructor will run synchronously. 
 Only the resolution itself (a call to resolve or reject) is asynchronous.
  54. Promises are Reliable Promise are inherently guaranteed to be resolvable

    once to a single value, success OR error Once resolved to a value, they become immutable - their state cannot be changed - they always resolve to that same value
  55. var promise = new Promise(function(resolve, reject) { resolve(1); // resolve

    once resolve(2); // resolve second time (this is ignored) reject(3); // this is also ignored });
 promise.then(function(result) { console.log(result); // 1 });
 promise.then(function(result) { console.log('still ' + result); // still 1 }); Resolve once
  56. Resolve to the same value var promise = new Promise(function(resolve,

    reject) { resolve({ greet: 'Hi, WordCamp!' }); });
 promise.then(function(result) { console.log(result.greet); // 'Hi, WordCamp!' result.greet = 'Oops'; });
 promise.then(function(result) { console.log(result.greet); // Oops });
  57. Promises are chainable We can respond to promises in a

    desired order, regardless of when they actually resolve - Every then call returns a Promise Sequential Flow - Returning a Promise from then will append it to the promise chain - Returning a non-thenable value from then will make it the next resolved value in the chain and proceed down the chain. Vertical Chain -> Solves “Pyramid of Doom”
  58. Avoid Pyramid of Doom Promise chains are meant to be

    vertical, nesting them is an anti-pattern function getThing(arg) { return new Promise(function(resolve, reject) { getStuff(arg) .then(function(stuff) { getMoreStuff(stuff) .then(function(moreStuff) { getThatFinalThing(moreStuff) .then(resolve, reject) }, reject) }, reject) }); } function getThing(arg) { return getStuff(arg) .then(getMoreStuff) .then(getThatFinalThing); } ===
  59. resolve(res); reject(err); }); new Promise(function(resolve, reject) { function httpGetPromise(url) {

    return new Promise(function(resolve, reject) { httpGet(url, function(err, res) { if (err) { reject(err); } else { resolve(res); } }); }); } How do we convert a callback utility to Promises? 1. Wrap the utility in a Promise constructor 2. Call resolve on success and reject on error
  60. processValues(values[0], values[1]); results.then var results = Promise.all([promise1, promise2]); var promise2

    = httpGetPromise(url2); var promise1 = httpGetPromise(url1); var promise2 = httpGetPromise(url2); var results = Promise.all([promise1, promise2]); results.then(function(values) { processValues(values[0], values[1]); }); var promise1 = httpGetPromise(url1); Promises - Getting two values in parallel Create promise2 and fire a request Create promise1 and fire a request
  61. var promise1 = httpGetPromise(url1); var promise2 = httpGetPromise(url2); var results

    = Promise.all([promise1, promise2]); results.then(function(values) { processValues(values[0], values[1]); }); Maintain state internally Doesn’t mix input and output DRY Sequential Flow Promises - Getting two values in parallel
  62. Promises - Getting values in parallel (generalized) var promises =

    urls.map(function(url) { return httpGetPromise(url); }); var results = Promise.all(promises); results.then(function(values) { processValues.apply(this, values); }); Maintain state internally Doesn’t mix input and output DRY Sequential Flow
  63. return promise2; processValue(value2); function(value2) { } processValue(value1); function(value1) { }

    // logging value1 then value2, // regardless of which value is
 // received first promise1 .then(function(value1) { processValue(value1); }) .then(function() { return promise2; }) .then(function(value2) { processValue(value2); }); function() { } Getting two values in parallel, process them in order // concurrent ("parallel") requests let promise1 = httpGetPromise(url1); let promise2 = httpGetPromise(url2); value1 received first 1. receive value1 2. process value1 5. receive value2 6. process value2 3. proceed down the chain 4. Wait on promise2
  64. return promise2; processValue(value2); function(value2) { } processValue(value1); function(value1) { }

    // logging value1 then value2, // regardless of which value is
 // received first promise1 .then(function(value1) { processValue(value1); }) .then(function() { return promise2; }) .then(function(value2) { processValue(value2); }); function() { } Getting two values in parallel, process them in order // concurrent ("parallel") requests let promise1 = httpGetPromise(url1); let promise2 = httpGetPromise(url2); value2 received first 1. receive value2 2. receive value1 5. Wait on promise2 6. process value2 3. process value1 4. proceed down the chain
  65. Promises can be chained into consistent Sequential Execution Flow, no

    matter the order of their internal asynchronous operations We choose in which order to process results, no matter when they arrive
  66. urls .map(httpGetPromise) .reduce(function(chain, promise) { return chain .then(function() { return

    promise; }) .then(processValue); }, Promise.resolve()); }); Maintain state internally Doesn’t mix input and output DRY Sequential Flow
  67. Error Handling Errors thrown from promises are handled by the

    second parameter (reject) passed to then or by the handler passed to catch Errors thrown from promises skip all resolve handlers until a reject handler or catch A promise enters the rejection state when we manually call the reject function or when a JS exception happens
  68. Error Handling var promise = new Promise(function(resolve, reject) { reject(Error('an

    error')); }); promise.then(function() { // not called }, function(err) { err; // 'an error' }); promise.catch(function(err) { err; // 'an error' });
  69. Error Handling var promise = new Promise(function(resolve, reject) { 2.method();

    }); promise.then(function() { // not called }, function(err) { err; // 'Uncaught SyntaxError: Invalid or unexpected token' }); promise.catch(function(err) { err; // 'Uncaught SyntaxError: Invalid or unexpected token' });
  70. Error Handling var promise = new Promise(function(resolve, reject) { 2.method();

    }); promise.then(function() { // not called }, function(err) { err; // 'Uncaught SyntaxError: Invalid or unexpected token' })
 .catch(function(err) { // Doesn’t get called because it was handled above });
  71. Error Handling function getJSON(url, cb) { httpGet(url, function(err, res) {

    if (err) return cb(err); try { res = JSON.parse(res); } catch(err) { return cb(err); } cb(null, res); }); } function getJSON(url) { return httpGetPromise(url) .then(JSON.parse); }
  72. urls .map(httpGetPromise) .reduce(function(chain, promise) { return chain .then(function() { return

    promise; }) .then(processValue) .catch(function(err) { // err is the error from the current promise }); }, Promise.resolve()); }); Error Handling
  73. Ok, Promises are pretty good but still have a few

    issues Can we do better? Verbose code Non linear readability
  74. Generators Pausable functions - A special type of function that

    can start and stop execution one or more times and even never finish Breaks the Run-to-Completion Semantic When combined with Promises they further improve the Sequential Flow of async code
  75. Generators Generators are functions which can be exited and later

    re-entered. Their context will be saved across re-entrances. We can push out multiple values from them
  76. Generators function* gen() { yield 'pause here'; return 'I am

    done'; } Calling the generator function returns an iterator. var iter = gen(); Calling .next() on an iterator returns an object with a value and a done flag iter.next(); // { value: any, done: boolean }
  77. Generators yield is a the pause/resume point of a generator

    Calling .next() on the iterator provided by the generator, runs the code within the generator from the current paused point (yield or start of the function) to the next pause point (yield or end of the function) Anything passed into .next() gets pushed into the generator as the return value from yield Anything passed into yield gets pulled from the generator as the return value from iterator’s .next()
  78. function* gen( ) { console.log( ); console.log('yield 1 - '

    + ); console.log('yield 2 - ' + ); console.log('end'); return 'C'; } var iter = gen(0); console.log({ value: undefined, done: true }); console.log('yield 1 - ' + ); console.log('yield 2 - ' + ); 'c' 'b' (yield 'A') console.log(iter.next('c')); console.log(iter.next('b')); var iter = gen(0); console.log({ value: 'A', done: false }); console.log({ value: 'A', done: false }); console.log(iter.next('a')); console.log({ value: 'C', done: true }); console.log({ value: 'C', done: true }); console.log({ value: 'B', done: false }); console.log({ value: 'B', done: false }); iter.next('d') return 'C'; console.log('end'); iter.next('c') (yield 'B') (yield 'A') iter.next('b') iter.next('a') console.log( ); 0 s s 0 (yield 'B') cc1BVTF cc1BVTF console.log(iter.next('d')); Console { value: 'A', done: false } 0 'yield 1 - b' { value: 'B', done: false } 'yield 2 - c' 'end' { value: 'C', done: true } { value: undefined, done: true }
  79. Well… What if a generator pushes out a Promise…. since

    generators pause and push on yield and wait for a pull so they can continue…. and then continues running when the Promise resolves?
  80. Async/Await Generators + Promises, except we replace * with async

    and yield with await Async function always returns a Promise Async function can await for any value, not just a Promise
  81. await httpGetPromise(urls[i]) await httpGetPromise(urls[i]) async function httpGetGen(urls) { for (var

    i = 0; i < urls.length; i++) { processValue(await httpGetPromise(urls[i])); } } httpGetGen(['google.com', 'yahoo.com']); processValue( ); processValue( ); httpGetGen(['google.com', 'yahoo.com']); Create promise1 and fire a request to google.com Create promise2 and fire a request 
 to yahoo.com Async/await - Chained requests
  82. async function httpGetGen(urls) { for (var i = 0; i

    < urls.length; i++) { processValue(await httpGetPromise(urls[i])); } } Maintain state internally Doesn’t mix input and output DRY Sequential & Synchronous Flow Async/await - Chained requests
  83. await Promise.all([promise1, promise2]); async function httpGetGen(url1, url2) { var promise1

    = httpGetPromise(url1); var promise2 = httpGetPromise(url2); var values = await Promise.all([promise1, promise2]); processValues(values[0], values[1]); } httpGetGen('google.com', 'yahoo.com'); httpGetGen('google.com', 'yahoo.com'); processValues(values[0], values[1]); values var promise2 = httpGetPromise(url2); var promise1 = httpGetPromise(url1); Create promise1 and fire a request to google.com Create promise2 and fire a request to yahoo.com Async/await - Getting two values in parallel
  84. await Promise.all([promise1, promise2]); async function httpGetGen(url1, url2) { var promise1

    = httpGetPromise(url1); var promise2 = httpGetPromise(url2); var values = await Promise.all([promise1, promise2]); processValues(values[0], values[1]); } Async/await - Getting two values in parallel Maintain state internally Doesn’t mix input and output DRY Sequential & Synchronous Flow Only pauses code within the function
  85. Getting values in parallel (generalized & ES6) async function httpGetGen(url)

    { const values = await Promise.all(urls.map(httpGetPromise)); processValues(...values); } Maintain state internally Doesn’t mix input and output DRY Sequential & Synchronous Flow
  86. Getting two values in parallel, process them in order async

    function httpGetGen(url1, url2) { var promise1 = httpGetPromise(url1); var promise2 = httpGetPromise(url2); processValue(await promise1); processValue(await promise2); } Maintain state internally Doesn’t mix input and output DRY Sequential & Synchronous Flow
  87. async function httpGetGen(urls) { var promises = urls.map(httpGetPromise); for (var

    i = 0; i < promises.length; i++) { processValue(await promises[i]); } } Maintain state internally Doesn’t mix input and output DRY Sequential & Synchronous Flow
  88. async function httpGetGen(urls) { var promises = urls.map(httpGetPromise); for (var

    i = 0; i < promises.length; i++) { try { processValue(await promises[i]); } catch(e) { // e is the error from the current promise } } } Error Handling Asynchronous error handling looks synchronous
  89. Chaining Since async functions return a Promise We could chain

    them like Promises We can also await for them
  90. async function httpGetAsync(urls) { var promises = urls.map(httpGetPromise); var values

    = []; for (var i = 0; i < promises.length; i++) { values.push(await promises[i]); } } async function processValues(urls) { var values = await httpGetAsync(urls); // ... do stuff with out values }
  91. Async/await eliminates the syntactic split Callbacks syntactically and behaviorally split

    code into “now” and ”later” Promises eliminate the behavioral split
  92. Sequential Flow Reliable Eliminate time as a concern - We

    can imagine as if we already have the result, when it arrives is irrelevant. Async/await Eliminate asynchronicity as a concern - We can imagine as if we are performing operations synchronously
  93. Published more thoughts on Medium blog.enki.com - Developing Async Sense

    in JavaScript Part I: Sync vs Async, Threads and the Event Loop Part II: Callbacks Part III: Promises
  94. Additional resources https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ What the heck is the Even Loop

    (https://www.youtube.com/watch?v=8aGhZQkoFbQ) https://blog.risingstack.com/writing-a-javascript-framework-execution-timing-beyond- settimeout/ https://github.com/getify/You-Dont-Know-JS http://www.html5rocks.com/en/tutorials/es6/promises/ Promise Visualizer (http://bevacqua.github.io/promisees/) https://ponyfoo.com/articles/understanding-javascript-async-await