James Hunter

@cccc00

ES7 Async Functions

But, How did we get here?

We Divided Cats...

Hologrammed Some JS...

A Man Love Promises...

I <3 Promises Too

Don't worry Ben

I <3 Promises Too

You will see why in about 2 minutes

Lets Create a Comment

Lets Create a Comment

  1. Check if user can comment
  2. Check if comment is valid
  3. Post comment to server
  4. If successful return comment

Synchronous World

function createComment (comment) {
  const canComment = userCanCreateComment();             // 1 Permission Check
  if (!canComment) throw new Error("User cant comment");
      
  const validComment = commentIsValid(comment);          // 2 Validate Comment
  if (!validComment) throw new Error("Invalid Comment");
  
  const success = postNewComment(comment);               // 3 Post to server
  if (!success) throw new Error("Could not save post");

  return comment;                                        // 4 return comment
}

Synchronous World

  • Very close to our list of things
  • Easy to follow
  • Easy to extend/modify
  • Arguably maintainable
  • Composable (with side effects)

Synchronous World

Callbacks

function createComment (comment, callback) {
  try {
    userCanCreateComment((err) => {           // 1 Permission Check
      // if err

      commentIsValid(comment, (err) => {      // 2 Validate Comment
        // if err
      
        postNewComment(comment, (err) => {    // 3 Post to server
          // if err

          if (callback) callback(comment);    // 4 return comment
        });
      });
    });
  } catch (err) {
    // do something with error
  }
}

Callbacks

  • Callback hell and the Pyramid of Doom
  • Higher order functions nested in higher order functions
  • Dealing with raised errors suck
  • Convention based ie: (error, result) => {}

Callbacks

function createComment (comment, callback) {
  try {
    userCanCreateComment((err) => {           // 1 Permission Check
      // if err

      commentIsValid(comment, (err) => {      // 2 Validate Comment
        // if err
      
        postNewComment(comment, (err) => {    // 3 Post to server
          // if err

          if (callback) callback(comment);    // 4 return comment
        });
      });
    });
  } catch (err) {
    // do something with error
  }
}

Doom

Promises

function createComment (comment) {
  return new Promise ((resolve, reject) => {
    userCanCreateComment()                                // 1 Permission Check
      .then(commentIsValid(comment), handleBadPermission) // 2 Validate Comment
      .then(postNewComment(comment), handleBadComment)    // 3 Post to Server
      .then(() => resolve(comment),  handleBadPost)       // 4 Return Comment

      .catch(err => reject(err, data))
  })
}

// but we obscure the complexity away

function commentIsValid(comment) {
  return function() {
    return new Promise ((resolve, reject) => {
        
      // the thing that actually checks if
      // the comment is valid
      return validatorThing(comment)
    })
  };
}

Promises

  • Notoriously hard to debug
  • Obscures complexity
  • Applicative Functor / Monad
  • The bigger the chain, the easier the fail

Enter Coroutines

What is a Coroutine ?

What is a Coroutine ?

A coroutine is a function that can suspend its execution (yield/wait) until the given Yield Instruction is completed

Coroutine w/ Generator

function createComment (comment) {
  return coroutine(function * () {
    const canComment = yield userCanCreateComment();       // 1 Permission Check
    if (!canComment) throw new Error("User cant comment");
    
    const validComment = yield commentIsValid(comment);    // 2 Validate Comment
    if (!validComment) throw new Error("Invalid Comment");

    const success = yield postNewComment(comment);         // 3 Post to Server
    if (!success) throw new Error("Could not save post");

    return comment;                                        // 4 Return Comment
  })();
}

Synchronous World

function createComment (comment) {
  const canComment = userCanCreateComment();             // 1 Permission Check
  if (!canComment) throw new Error("User cant comment");
      
  const validComment = commentIsValid(data);             // 2 Validate Comment
  if (!validComment) throw new Error("Invalid Comment");
  
  const success = postNewComment(comment);               // 3 Post to server
  if (!success) throw new Error("Could not save post");

  return comment;                                        // 4 return comment
}

Coroutine w/ Generator

  • Closest yet to our synchronous example
  • Useable today Bluebird.coroutine(generator)
  • Must yield to a Promise
  • Leverages promises in a new powerful way
  • Need to polyfill generators (Regenerator)
  • Coroutine returns a function that returns a Promise

ES7 Async & Await

async function createComment (comment) {
  const canComment = await userCanCreateComment();       // 1 Permission Check
  if (!canComment) throw new Error("User cant comment");
    
  const validComment = await commentIsValid(comment);    // 2 Validate Comment
  if (!validComment) throw new Error("Invalid Comment");

  const success = await postNewComment(comment);         // 3 Post to Server
  if (!success) throw new Error("Could not save post");

  return comment;                                        // 4 Return Comment
}

ES7 Async & Await

      function createComment (comment) {
  const canComment =       userCanCreateComment();       // 1 Permission Check
  if (!canComment) throw new Error("User cant comment");
    
  const validComment =       commentIsValid(comment);    // 2 Validate Comment
  if (!validComment) throw new Error("Invalid Comment");

  const success =       postNewComment(comment);         // 3 Post to Server
  if (!success) throw new Error("Could not save post");

  return comment;                                        // 4 Return Comment
}

Synchronous World

function createComment (comment) {
  const canComment = userCanCreateComment();             // 1 Permission Check
  if (!canComment) throw new Error("User cant comment");
    
  const validComment = commentIsValid(comment);          // 2 Validate Comment
  if (!validComment) throw new Error("Invalid Comment");

  const success = postNewComment(comment);               // 3 Post to Server
  if (!success) throw new Error("Could not save post");

  return comment;                                        // 4 Return Comment
}

ES7 Async & Await

  • Extremely close to our synchronous example
  • Useable today with transpilers (Babel)
  • Does the same thing as our Coroutine example
  • Must await on promises

Coroutines

  • rationalize asynchronous code closer to how we think
  • utilize asynchronous functions similar to synchronous ones
  • leverage promises in a less error prone way
  • are composable in other coroutines

Resources

  • Slides – slides.com/jameshunter/async-await
  • Coroutines – en.wikipedia.org/wiki/Coroutine
  • Bluebird – github.com/petkaantonov/bluebird
  • Babel – babeljs.io
  • tc39 Proposal – github.com/tc39/ecmascript-asyncawait
  • Compatibility – kangax.github.io/compat-table/es7
  • Twitter – James Hunter – @cccc00

Async & Await

By James Hunter

Async & Await

  • 935