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:
functiongetBooksFromAPI(){returnfetch(BOOK_API_ENDPOINT);}asyncfunctiongetBookTitles(){try{constbookData=awaitgetBooksFromAPI();}catch{thrownewError("Book API unavailable");}returnbookData.map(book=>book.title);}
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."
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!
A software engineer living in Edinburgh, Scotland. I'm passionate about programming, learning new things (and sharing what I learn), digital art, and good sci-fi/fantasy books! 💻📚🎨✨
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:
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
constunBoxed=awaitmyPromise;
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.
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:
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.
Top comments (11)
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:
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 anawait,
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 atry/catch
block to deal with that.*try/catch
isn't the only way to deal withUnhandled 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
await
ing something asynchronous (the API results) then it isn't going to return synchronously! So it needs to return a promise, too. Theasync
keyword automatically wraps the return value of a function in a promise. If you're usingawait
in a function, you need to mark it using theasync
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."
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...
thanks a lot @Betsy . It's really awesome !!
This is a fantastic explanation!
Before understanding async/await you should brush up on promises for 5 year olds first:
Explain JavaScript Promises like I am five.
Chinmay Joshi ・ Sep 20 '18 ・ 1 min read
(K and Benny Powers give really good explanations there)
There's also this (even if it's not for 5 year olds):
All about Promises and async / await
Arden de Raaij ・ Jan 20 '18 ・ 14 min read
Thanks @chinmay for sharing.
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:How to Serialize Concurrent Operations in JavaScript: Callbacks, Promises, and Async/Await
Nested Software
Careful Examination of JavaScript Await
Nested Software
Thanks a lot @Nested software for sharing this article.
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 thereturn
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
inside an async function,
myPromise
is the box that holds the future data, andunBoxed
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.
thanks a lot @neil
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:
Let's try to codify those as functions:
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()
, andputShoesOn()
, 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:
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.thanks for this awesome answer @david