Neato Burrito: Javascript Promises and Async/Await

Promises were one of the first concepts in JavaScript that really made my brain melt. What were they, and why did they seem so mystifying? I had used them, but I sure as heck didn’t understand what it was that they did. But then I was taught in my DigitalCrafts class to think of the assembly line at Chipotle, and everything seemed to click into place.

Before I jump into that seemingly strange analogy, let me do a quick recap on promises. Before we had promises, we handled asynchronous calls using callback functions. Asynchronous, or "async," is just a fancy way of saying, “Hey, this response you want is gonna take a minute, so come back later.”

These types of functions are usually used when we are working with APIs. Asynchronous calls return a result, like all functions, but they take a little longer to produce the result. These callback functions would get nested in other callback functions. When the first function was done running, it would run the second function and on and on down the line. This resulted in what we called "callback hell," like so:

Make sense?

Uhhh…. huh?

This would usually be harder to debug and a really gross way of building functions. But then, JavaScript ES6 came along and introduced promises, providing a cleaner way handle asynchronous calls using neater syntax.

Now, back to that Chipotle analogy. imagine you walk into Chipotle, and you walk up to the first person behind the counter and you order a burrito. The first person behind the counter grabs a tortilla, warms it up and then passes it to the next person in the assembly line. That second person adds beans and rice and passes that onto the next person in the assembly line. The third person adds meat and toppings before wrapping everything up and handing it to the cashier. The cashier rings it up and gives you your burrito, with all the delicious fillings you ordered. This is kind of how a promise chain works.

A promise is an object that can be in a "one-of-three" state, meaning that it can be pending, fulfilled or rejected. If it’s pending, we are just letting the Chipotle worker do their thing while they are getting our order together. Once you ordered a burrito, the first worker will fetch a tortilla, warm it up and once they are done, their job changes from pending to fulfilled, because they have successfully resolved your request. Alternately, the worker might turn to you and reject your request for a burrito because they didn’t hear you or they are sold out of tortillas — a sad day for sure.

In JavaScript, we can use the fetch function to kick start our "order." We give the fetch function a URL and it fetches the data we requested from that URL and returns a promise. The promise is handled when it is fulfilled.

Once we have gotten our response back, we chain on a ‘.then( )’ function to give instructions of what to do next. The ‘.then’ function expects to receive a function (or set of instructions) to be executed when the promise has been fulfilled. We also will usually add a ‘.catch’ function to the promise chain to be executed if and when a promise has been rejected.

Promise chain analogy in code

The fetch function is a little funny in JavaScript. When we use the fetch function, we are only getting the response object (pending, fulfilled or rejected), not the actual usable data. We have to ask for the data coming back to be transformed into JSON data we can use. In order to extract that JSON body content, we use the ‘json()’  function, which also returns a promise. Once we have the JSON data, we can keep manipulating the data along the promise chain until we get the result we want.

One thing to remember is that as we go down the chain, we need to make sure that we use the return keyword down the line so the next ‘.then()’ function has access to it. If we don’t return anything, it’s basically as if the first Chipotlle burrito fetches our tortilla, hands it to the next person in line. This second person adds some beans and then throws your burrito on the floor and nothing gets passed down to the next guy. How sad!

Async/ Await

With ES8, we got some syntactic sugar called ‘async/ await’ to clean up our Promises. "Syntax sugar" means that we didn’t get any new functionality, but we got a shorter, nicer-looking way to write asynchronous functions. Below is an example of how we would implement async/await on our previous burrito building function:

Our Promise analogy as async/await

The code inside the async function is put on pause with await. The await functions wait until the function/’promise’ is successful or rejected and then move down the line. An important thing to remember is that ‘await’ can only be used inside of an async function block.

There is more detail that we could go into about async/await, like how we handle errors, but this is a good starting point. It’s kind of like the promise chain we saw previously, but with cleaner syntax that is more readable and easier to debug.

Amazing, I know.

Promises can be a tricky concept to wrap your head around. Hopefully, this article not only made you hungry for burritos, but you understand how we solved the problem of callback hell with the implementation of Promises. In order to understand async/await, we have to understand Promises and how they function. And in order to understand the flow of Promises, you should always think of the friendly workers at Chipotle building you a tasty burrito… and may they never be out of tortillas or throwing your burrito on the floor.

This post was written by Aylin DeBruyne, Developer in Residence.


Student Blogger

Articles written by the students and Alumni of DigitalCrafts