Understanding Async Await

When writing code for the web, eventually you'll need to do some process that might take a few moments to complete. JavaScript can't really multitask, so we'll need a way to handle those long-running processes.

Async/Await is a way to handle this type of time-based sequencing. It’s especially great for when you need to make some sort of network request and then work with the resulting data. Let's dig in!

Promise? Promise.

Async/Await is a type of Promise. Promises in JavaScript are objects that can have multiple states (kind of like the real-life ones ☺️). Promises do this because sometimes what we ask for isn't available immediately, and we'll need to be able to detect what state it is in.

Consider someone asks you to promise to do something for them, like help them move. There is the initial state, where they have asked. But you haven't fulfilled your promise to them until you show up and help them move. If you cancel your plans, you rejected the promise.

Similarly, the three possible states for a promise in JavaScript are:

  • pending: when you first call a promise and it’s unknown what it will return.
  • fulfilled: meaning that the operation completed successfully
  • rejected: the operation failed

Here’s an example of a promise in these states:

Here is the fulfilled state. We store a promise called getSomeTacos, passing in the resolve and reject parameters. We tell the promise it is resolved, and that allows us to then console log two more times.

const getSomeTacos = new Promise((resolve, reject) => {
  console.log("Initial state: Excuse me can I have some tacos");

  resolve();
})
  .then(() => {
    console.log("Order some tacos");
  })
  .then(() => {
    console.log("Here are your tacos");
  })
  .catch(err => {
    console.error("Nope! No tacos for you.");
  });
> Initial state: Excuse me can I have some tacos
> Order some tacos
> Here are your tacos

See the Pen
Promise States
by Sarah Drasner (@sdras)
on CodePen.

If we choose the rejected state, we'll do the same function but reject it this time. Now what will be printed to the console is the Initial State and the catch error:

const getSomeTacos = new Promise((resolve, reject) => {
  console.log("Initial state: Excuse me can I have some tacos");

  reject();
})
  .then(() => {
    console.log("Order some tacos");
  })
  .then(() => {
    console.log("Here are your tacos");
  })
  .catch(err => {
    console.error("Nope! No tacos for you.");
  });
> Initial state: Excuse me can I have some tacos
> Nope! No tacos for you.

And when we select the pending state, we'll simply console.log what we stored, getSomeTacos. This will print out a pending state because that's the state the promise is in when we logged it!

console.log(getSomeTacos)
> Initial state: Excuse me can I have some 🌮s
> Promise {<pending>}
> Order some &#x1f32e;s
> Here are your &#x1f32e;s

What then?

But here’s a part that was confusing to me at first. To get a value out of a promise, you have to use .then() or something that returns the resolution of your promise. This makes sense if you think about it, because you need to capture what it will eventually be — rather than what it initially is — because it will be in that pending state initially. That's why we saw it print out Promise {<pending>} when we logged the promise above. Nothing had resolved yet at that point in the execution.

Async/Await is really syntactic sugar on top of those promises you just saw. Here's a small example of how I might use it along with a promise to schedule multiple executions.

async function tacos() {
  return await Promise.resolve("Now and then I get to eat delicious tacos!")
};

tacos().then(console.log)

Or a more in-depth example:

// this is the function we want to schedule. it's a promise.
const addOne = (x) => {
  return new Promise(resolve => {
    setTimeout(() => { 
      console.log(`I added one! Now it's ${x + 1}.`)
      resolve()
    }, 2000);
  })
}

// we will immediately log the first one, 
// then the addOne promise will run, taking 2 seconds
// then the final console.log will fire
async function addAsync() {
  console.log('I have 10')
  await addOne(10)
  console.log(`Now I'm done!`)
}

addAsync()
> I have 10
> I added one! Now it's 11.
> Now I'm done!

See the Pen
Async Example 1
by Sarah Drasner (@sdras)
on CodePen.

One thing (a)waits for another

One common use of Async/Await is to use it to chain multiple asynchronous calls. Here, we'll fetch some JSON that we'll use to pass into our next fetch call to figure out what type of thing we want to fetch from the second API. In our case, we want to access some programming jokes, but we first need to find out from a different API what type of quote we want.

The first JSON file looks like this- we want the type of quote to be random:

{
  "type": "random"
}

The second API will return something that looks like this, given that random query parameter we just got:

{
  "_id":"5a933f6f8e7b510004cba4c2",
  "en":"For all its power, the computer is a harsh taskmaster. Its programs must be correct, and what we wish to say must be said accurately in every detail.",
  "author":"Alan Perlis",
  "id":"5a933f6f8e7b510004cba4c2"
}

We call the async function then let it wait to go retrieve the first .json file before it fetches data from the API. Once that happens, we can do something with that response, like add it to our page.

async function getQuote() {
  // get the type of quote from one fetch call, everything else waits for this to finish
  let quoteTypeResponse = await fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`)
  let quoteType = await quoteTypeResponse.json()

    // use what we got from the first call in the second call to an API, everything else waits for this to finish
  let quoteResponse = await fetch("https://programming-quotes-api.herokuapp.com/quotes/" + quoteType.type)
  let quote = await quoteResponse.json()

  // finish up
  console.log('done')
}

We can even simplify this using template literals and arrow functions:

async function getQuote() {
  // get the type of quote from one fetch call, everything else waits for this to finish
  let quoteType = await fetch(`quotes.json`).then(res => res.json())

    // use what we got from the first call in the second call to an API, everything else waits for this to finish
  let quote = await fetch(`programming-quotes.com/${quoteType.type}`).then(res => res.json())

  // finish up
  console.log('done')
}

getQuote()

Here is an animated explanation of this process.

See the Pen
Animated Description of Async Await
by Sarah Drasner (@sdras)
on CodePen.

Try, Catch, Finally

Eventually we’ll want to add error states to this process. We have handy try, catch, and finally blocks for this.

try {
  // I’ll try to execute some code for you
}
catch(error) {
  // I’ll handle any errors in that process
} 
finally {
  // I’ll fire either way
}

Let’s restructure the code above to use this syntax and catch any errors.

async function getQuote() {
  try {
    // get the type of quote from one fetch call, everything else waits for this to finish
    let quoteType = await fetch(`quotes.json`).then(res => res.json())

      // use what we got from the first call in the second call to an API, everything else waits for this to finish
    let quote = await fetch(`programming-quotes.com/${quoteType.type}`).then(res => res.json())

    // finish up
    console.log('done')
  }

  catch(error) {
    console.warn(`We have an error here: ${error}`)
  }
}

getQuote()

We didn’t use finally here because we don’t always need it. It is a block that will always fire whether it is successful or fails. Consider using finally any time you’re duplicating things in both try and catch. I usually use this for some cleanup. I wrote an article about this, if you’re curious to know more.

You might eventually want more sophisticated error handling, such as a way to cancel an async function. There is, unfortunately, no way to do this natively, but thankfully, Kyle Simpson created a library called CAF that can help.

Further Reading

It's common for explanations of Async/Await to begin with callbacks, then promises, and use those explanations to frame Async/Await. Since Async/Await is well-supported these days, we didn’t walk through all of these steps. It’s still pretty good background, especially if you need to maintain older codebases. Here are some of my favorite resources out there:

The post Understanding Async Await appeared first on CSS-Tricks.

A Peek at New Methods Coming to Promises

Promises are one of the most celebrated features introduced to JavaScript. Having a native asynchronous artifact baked right into the language has opened up a new era, changing not only how we write code but also setting up the base for other freat APIs — like fetch!

Let's step back a moment to recap the features we gained when they were initially released and what new bells and whistles we’re getting next.

New to the concept of Promises? I highly recommend Jake Archibald’s article as a primer.

Features we have today

Let’s take a quick look at some of the things we can currently do with promises. When JavaScript introduced them, it gave us an API to execute asynchronous actions and react to their succesful return or failure, a way to create an association around some data or result which value we still don't know.

Here are the Promises features we have today.

Handling promises

Every time an async method returns a promise — like when we use fetch — we can pipe then() to execute actions when the promise is fulfilled, and catch() to respond to a promise being rejected.

fetch('//resource.to/some/data')
  .then(result => console.log('we got it', result.json()))
  .catch(error => console.error('something went wrong', error))

The classic use case is calling data from an API and either loading the data when it returns or displaying an error message if the data couldn’t be located.

In addition, in its initial release we got two methods to handle groups of Promises.

Resolving and rejecting collections of promises

A promise can be fulfilled when it was successfully resolved, rejected when it was resolved with an error, and pending while there’s no resolution. A promise is considered settled when it has been resolved, disregarding the result.

As such, there are two methods we have to help with the behavior of handling a group of promises depending on the combination of states we obtain.

Promise.all is one or those methods. It fulfills only if all promises were resolved successfully, returning an array with the result for each one. If one of the promises fails, Promise.all will go to catch returning the reason of the error.

Promise.all([
    fetch('//resource.to/some/data'),
    fetch('//resource.to/more/data')
  ])
  .then(results => console.log('We got an array of results', results)
  .catch(error => console.error('One of the promises failed', error)

In this case, Promise.all will short-circuit and go to catch as soon as one of the members of the collections throws an error, or settle when all promises are fulfilled.

Check out this short writing about promises states by Domenic Denicola for a more detailed explanation about the wording and concepts about them.

We also have Promise.race, which immediately resolves to the first promise it gets back, whether it was fulfilled or rejected. After the first promise gets resolved, the remaining ones are ignored.

Promise.race([
    fetch('//resource.to/some/data'),
    fetch('//resource.to/other/data')
  ])
  .then(result => console.log('The first promise was resolved', result))
  .catch(reason => console.error('One of the promises failed because', reason))

The new kids on the block

OK, we’re going to turn our attention to new promise features we can look forward to.

Promise.allSettled

The next proposed introduction to the family is Promise.allSettled which, as the name indicates, only moves on when the entire collection members in the array are no longer in a pending status, whether they were rejected or fulfilled.

Promise.allSettled([
    fetch('//resource.to/some/data'),
    fetch('//resource.to/more/data'),
    fetch('//resource.to/even/more/data')
  ])
  .then(results => {
    const fulfilled = results.filter(r => r.status === 'fulfilled')
    const rejected = results.filter(r => r.status === 'rejected')
  })

Notice how this is different from Promise.all in that we will never enter in the catch statement. This is really good if we are waiting for sets of data that will go to different parts of a web application but want to provide more specific messages or execute different actions for each outcome.

Promise.any

The next new method is Promise.any, which lets us react to any fulfilled promise in a collection, but only short-circuit when all of them failed.

Promise.any([
    fetch('//resource.to/some/data'),
    fetch('//resource.to/more/data'),
    fetch('//resource.to/even/more/data')
  ])
  .then(result => console.log('a batch of data has arrived', result))
  .catch(() => console.error('all promises failed'))

This is sort of like Promise.race except that Promise.race short-circuits on the first resolution. So, if the first promise in the set resolves with an error, Promise.race moves ahead. Promise.any will continue waiting for the rest of the items in the array to resolve before moving forward.

Demo

Some of these are much easier to understand with a visual, so I put together a little playground that shows the differences between the new and existing methods.

Wrap-up

Though they are still in proposal stage, there are community scripts that emulate the new methods we covered in this post. Things like Bluebird’s any and reflect are good polyfills while we wait for browser support to improve.

They also show how the community is already using these kind of asynchronous patterns, but having them built-in will open the possibilities for new patterns in data fetching and asynchronous resolution for web applications.

Besides then and catch you can pipe finally to a promise, Sarah Drasner wrote a detailed piece about it that you can check out.

If you want to know more about the upcoming Promise combinators, the V8 blog just published a short explanation with links to the official spec and proposals.

Finally… A Post on Finally in Promises

“When does finally fire in a JavaScript promise?” This is a question I was asked in a recent workshop and I thought I’d write up a little post to clear up any confusion.

The answer is, to quote Snape:

snape saying always

...always.

The basic structure is like this:

try {
  // I’ll try to execute some code for you
}
catch(error) {
  // I’ll handle any errors in that process
} 
finally {
  // I’ll fire either way
}

Take, for instance, this example of a Chuck Norris joke generator, complete with content populated from the Chuck Norris Database API. (Aside: I found this API from Todd Motto’s very awesome list of open APIs, which is great for demos and side projects.)

See the Pen
finally! chuck norris jokes!
by Sarah Drasner (@sdras)
on CodePen.

async function getData() {
  try {
    let chuckJokes = await fetch(`https://api.chucknorris.io/jokes/random`)
      .then(res => res.json())
    
    console.log('I got some data for you!')
    document.getElementById("quote").innerHTML = chuckJokes.value;
  } 
  catch(error) {
    console.warn(`We have an error here: ${error}`)
  
  finally {
    console.log('Finally will fire no matter what!')
  
}

In the console:

Console that says: I got some data for you! and Finally will fire no matter what!

Now, let’s introduce a typo to the API and accidentally put a bunch of r's in the URL of the API. This will result in our try statement failing, which means the catch now throws an error.

async function getData() {
  try {
    // let's mess this up a bit
    let chuckJokes = await fetch(`https://api.chucknorrrrris.io/jokes/random`)
      .then(res => res.json())
    
    console.log('I got some data for you!')
    document.getElementById("quote").innerHTML = chuckJokes.value;
  } 
  catch(error) {
    console.warn(`We have an error here: ${error}`)
  }
  finally {
    console.log('Finally will fire no matter what!')
  }
}

Console:

Console that has a failed GET request and then a warning that says We have an error here, Typeerror: failed to fetch, and then on a new line, Finally will fire no matter what!

One giant important piece that the example doesn’t illustrate is that the finally block will run even if in the try or catch block, a return or break statement stops the code.

When would you use this?

I’ve found it particularly useful in two different situations, though I’m sure there are others:

  • When I otherwise would duplicate code that’s need in the try and catch blocks. Here’s an example in a Vue cookbook recipe I wrote. I shut off the loading state in a finally block. This includes, like the example above, where I need to change the UI somehow in either case.
  • When there’s some cleanup to do. Oracle mentions this in their documentation. It’s Java, but the same premises apply.

Finally is not useful as often as try and catch, but worth noting for some use cases. Hope that clears it up!