DEV Community

Cover image for Explain async and await of Javascript like I am five.
Rupesh Krishna Jha
Rupesh Krishna Jha

Posted on

Explain async and await of Javascript like I am five.

Top comments (11)

Collapse
 
bhaibel profile image
Betsy Haibel

There are a few different angles I could take here!

Let's start with why they exist, follow up with what they do, and then go (briefly) into how they do it.

Why

All modern JavaScript runtimes (browser environments, Node.js, etc) use asynchronous I/O models. I/O operations (like calls to external APIs, or receiving user input) are characterized by waiting -- until your computer gets that API result, it can't do anything with it! Asynchronous I/O means that, while a function is "waiting" for an API result, the JavaScript runtime can start doing something else in the meantime. This improves app performance. But it's also harder to think about! We're taught how to program in a synchronous style, where line 1 executes, then line 2, then line 3. In an asynchronous style, maybe line 1 will execute, then line 6, then line 2....

There are a lot of different ways that JavaScript's language designers have tried to cope with this problem, like event emitters, callbacks, and promises. Async/await is the newest way of trying to make asynchronous code easier to think about.

What

Async/await looks like this:

function getBooksFromAPI() {
  return fetch(BOOK_API_ENDPOINT);
}

async function getBookTitles() {
  try {
    const bookData = await getBooksFromAPI();
  } catch {
    throw new Error("Book API unavailable");
  }
  return bookData.map(book => book.title);
}
Enter fullscreen mode Exit fullscreen mode

The first function returns a promise from fetch. This promise represents something the runtime intends to do -- it intends to get books from the API endpoint. Eventually, it will resolve to either success (book data) or failure. But it may or may not have done so yet. In the second function , we use the await keyword to wait for the promise to "fulfill". When the runtime hits an await, it knows to pause that function (and go do something else) until that promise has resolved itself.

When the promise resolves, the runtime "unpauses" the function. If the promise resolves with success, then the book data will get put into the bookData constant. If it fails, it'll throw a really non-specific error: Unhandled promise rejection. So we wrap it in a try/catch block to deal with that.*

  • try/catch isn't the only way to deal with Unhandled promise rejection errors. There are a lot of better ways! But it's the quickest to explain, and it's the most common.

Then, the runtime moves on to the next line, as though we were in synchronous code. In this case, we want to map the book data and return only the titles. Since await converted the API result promise (which isn't the API result, merely to the computer saying that it intends to get us an API result someday) into the actual book data, this is easy!

BUT! Because this function relies on awaiting something asynchronous (the API results) then it isn't going to return synchronously! So it needs to return a promise, too. The async keyword automatically wraps the return value of a function in a promise. If you're using await in a function, you need to mark it using the async keyword, so that the interpreter can trust that it returns a promise... because otherwise, you might try to call it synchronously and get unexpected results.

Ooof, that's a lot. and the how is even bigger.

How

This is something I don't really think can be done comprehensively in an ELI5 way. But the core of it is an idea called "generators," which let you specifically tell the JavaScript runtime "pause here until I call you next."

function * evenNumberGenerator() {
  const number = 0;
  while (true) {
    yield number;
    number = number + 2;
  }
}

const numbers = evenNumberGenerator();
const zero = numbers.next().value;
const two = numbers.next().value;
Enter fullscreen mode Exit fullscreen mode

Since we "pause" every time we hit the yield keyword, the infinite loop is "safe" -- we can "unpause" with next, and know that we'll get the next number in the sequence when we "pause" again. The await keyword uses generators under the hood to ask promises whether they've resolved yet, so that you can "unpause" the parent function and move on to the next line.

More

I've written up a brief, free email course that goes deeper into what all of the asynchronous "stuff" in Javascript does, the history of it, and how to use it effectively -- that might be a good next place to go, if this explanation was useful to you!

wecohere.com/products/untangling-a...

Collapse
 
rupeshiya profile image
Rupesh Krishna Jha

thanks a lot @Betsy . It's really awesome !!

Collapse
 
_darrenburns profile image
Darren Burns

This is a fantastic explanation!

Collapse
 
rhymes profile image
rhymes

Before understanding async/await you should brush up on promises for 5 year olds first:

(K and Benny Powers give really good explanations there)

There's also this (even if it's not for 5 year olds):

Collapse
 
rupeshiya profile image
Rupesh Krishna Jha

Thanks @chinmay for sharing.

Collapse
 
nestedsoftware profile image
Nested Software • Edited

I don't feel like I can offer an explanation that is truly eli5. In a nutshell, async/await allows you to write asynchronous code in a way that looks a lot like imperative, synchronous code.

I wrote an article explaining first the reason for async/await, and how it fits in with callbacks and promises, as well as an article showing in detail what happens when you await a promise:

Collapse
 
rupeshiya profile image
Rupesh Krishna Jha

Thanks a lot @Nested software for sharing this article.

Collapse
 
mebble profile image
Neil Syiemlieh

I'll try to keep this short. I hope the basic idea will still get through though.

First, promises. Normally, you'd have some data in your code, which is assigned a variable name. That variable is just sitting there and you can do whatever you want to it, pass it to a function, print it to the console, etc. No limitations.

But what if you have the variable name, but the data that should be in it isn't available yet? What if that data will be available in the future? Well now, if you print the variable to the console, it might not show up! The data might be available much later, after you've issued the print command. This is where promises come in.

A promise is a box, a "safe space" where you can be sure the data will available. Inside this box, the data will be available and you can do whatever you want to that data. Outside this box, it isn't safe. The data isn't guaranteed to be available.

There's one special feature though. If you've got data inside a box that's within another box, simply opening the outer box will reveal to you the data. So you don't have to go through the burden of opening two boxes. It does it automatically for you! If this has confused you, you can forget this paragraph.

Now for the main bit. You can open the box, do whatever you want to the data, and close up the box again. That's what .then (opening the box) and the return within the .then callback (closing it up again) do in promises. Notice here that we don't actually take the data out of the box. It's not safe to do that. We just do stuff to it, and then close the box back up.

The async/await are just cleaner ways of doing the exact same thing. An async function is basically a "safe open space" where you can open up a box, be sure the data would be available in it and actually take out the data! The await is you opening up the box and taking out the data. So it's a .then with more freedom.

When you write

const unBoxed = await myPromise;

inside an async function, myPromise is the box that holds the future data, and unBoxed will hold that data when it is available. It's all gonna be safe since we're in our async safe space. At the end of the async function, when you return data to the outside unsafe world, the async function will automatically wrap that data up in a box to keep it safe. Hence, the return value of an async function is a promise.

Why do we need this safe space? This safe space is "the future". Code within this space will execute in the future. So it's obvious that we don't want it mixing with code that's gonna execute right now.

Collapse
 
rupeshiya profile image
Rupesh Krishna Jha

thanks a lot @neil

Collapse
 
pdlozano profile image
David Lozano

Might be a little late but I'll try.

You are going to an event. But before you do that, you must first put your clothes on. For simplicity, let's just say you'll wear the following:

  1. A shirt
  2. A pair of jeans
  3. A pair of socks
  4. A pair of shoes

Let's try to codify those as functions:

function putShirtOn() {}
function putJeansOn() {}
function putSocksOn() {}
function putShoesOn() {}
Enter fullscreen mode Exit fullscreen mode

Now, if you were to write synchronous code, the program would follow the flow from top to bottom. In other words, if you told your program to putShirtOn(), putJeansOn(), putSocksOn(), and putShoesOn(), it will do so in that EXACT order - even if there's no reason to.

You see, there's no reason why you can't put your jeans on before your shirt - and vice versa. So why not do both at the same time?

That's asynchronous programming.

Here's a visual courtesy of Cambridge Intelligence:

Visual comparing async vs sync

Now for await. You see, you can't put your shoes on without putting your socks on first. If you did, you'd have to remove your shoes back again to put the socks in.

This is where await comes in - it tells the function to really just wait for the data to come or for a function to finish before executing everything else.

Collapse
 
rupeshiya profile image
Rupesh Krishna Jha

thanks for this awesome answer @david