A brief introduction to Async Await



Asynchronous code is both a blessing, and a curse in JavaScript. Whilst it allows you to write more efficient code. Sometimes you need JavaScript to be synchronous. In other words, sometimes you need your JavaScript code to wait for something else to finish, before it can move onto the next line of code.

Since JavaScript doesn't support coroutines and channels, such as languages like Go, we need a clean way to tell our JavaScript to hold up for a second until the previous line has finished.

Walk with me in callback hell

In the ye olden days of JavaScript, it was common to see code like this...

getThings(arg, function(things) {  
  displayThings(things, function(success) {
    if (success) {
     done(Date.now(), function(id) {
       console.log(id)
     })
    }
  })
})

I.e 'callback hell'. This is where you pass in a function as an argument, and call that function, when the first function has completed. However, you could end up with several layers of callbacks, like some kind of crazy code pyramid. Not very easy to read or work with.

 Along came promises

Promises, promised to change the way we controlled the flow of asynchronous JavaScript, giving us a neat little wrapper to prevent callback hell:

getThings(arg)  
.then(things => displayThings(things))
.then(success => {
  if (success) {
    return done(Date.now())
  }
  throw new Error('Something bad happened')
})
.then(id => console.log(id))

To create a promise...

function getThings() {  
  return new Promise((resolve, reject) => {
    const results = [1, 2, 3, 4, 5]
    if (results.length > 0) {
      resolve(results)
    }

    const err = new Error('No results')
    reject(err)
  })
}

Promises allowed you to pass around a 'pending' promise as a variable, giving you much more flexibility about where in your code this is executed.

const res = getThings(arg)

// Some other code

res.then(things => displayThings(things))  

You could also create multiple promises, and execute them in series...

const promiseOne = getFirstThings()  
const promiseTwo = getSecondThings()  
const promiseThree = getThirdThings()

Promise.all([promiseOne, promiseTwo, promiseThree]).then(results => {  
  console.log(results)
}, err => {
  console.log(err)
})

Whilst this was much nicer to read, and understand, you still have everything scoped in these '.then()' functions. Which, whilst better than callbacks, still doesn't feel like a solution, as you can end up with layers of 'thens'.

 In comes Async/Await

Async await was a fantastic addition to the JavaScript language, it's supported in Node 6+ and is ES2016, so configure your babel set-up to support this feature. So what does it look like?

async function(arg) {  
  try {
    const response = await getThings(arg)
    const success = await displayThings(response)

    if (success) {
      const id = await done(Date.now())
      console.log(id)
    }

    throw new Error('Some error message')
  } catch (e) {
    console.log(e)
  }
}

Asides from looking much neater, this also has the added benefit of retaining the same scope. Which is handy.

 What does this look like in React?

import React from 'react'

class MyComponent extends React.Component {  
  constructor(props) {
    super(props)
  }

  componentDidMount() {
    const { id } = this.props.params
    this.fetchUser(id)
  }

  fetchUser = async(id) => {
    try {
      const res = await fetch(`api/v1/users/${id}`)
      const user = await res.json()
      this.setState({ user })
    } catch (e) {
      this.setState({ err: e.message })
    } 
  }

  render() {
    return (
      <div>
        <p>Welcome, {(user ? user.name : "new user")}</p>
      </div>
    )
  }
}

export default MyComponent  

This is a completely trivial React example, but as you can see it's quite neat, and easy to follow.

Here's an example using async await in Redux actions...

export const getArticles = () => async (dispatch) => {  
  try {
    dispatch({ type: 'GET_ARTICLES' });
    const res = await fetch.get('/articles')
    const articles = await req.json()
    dispatch({ 
      type:'GOT_ARTICLES', 
      payload: articles, 
    })
  } catch(e) {
    dispatch({ 
      type: 'GET_ARTICLES_FAIL', 
      payload: e.message, 
    })
  }
}

Whilst this is welcomed approach, async await is really just an abstraction over the top of promises under the hood. So arguably the only real benefit to async await is that it's nicer to read and write, but that's just fine.

Note: use of the async keyword with fat arrow functions only seems to be supported when using babel. When using Node 6/7, you will need to use non fat-arrow functions. I.e...

async myFunction(arg) {  
  // etc
}