How JavaScript Async/Await Works Under the Hood

For JavaScript Web Developers

Photo: https://commons.wikimedia.org/wiki/File:JavaScript-logo.png

Introduction

The web is asynchronous in nature. User interfaces wait for user inputs. Servers wait for network requests. Databases wait for queries and mutations. The JavaScript language understands this, providing Promises in the ES6 specification to simplify asynchronous programming. Even more exciting is Async/Await coming in ES8 that further streamlines the behavior of using Promises.

For example, the Fetch API provides an interface for making network requests, returning Promises for us to resolve. To use it to get the current weather in Long Beach, we chain together two then callbacks:

In the first, we create a callback function to receive the Response object and return back its JSON data. In the second, we create a callback that receives that data and then logs it to the console.

Using Async/Await, the same code becomes:

By wrapping the logic inside an async function, we can replace the then callbacks with await statements. The effect, the code pauses execution on those lines until the Promises resolve! Asynchronous programming becomes synchronous! 😲

Believe it or not, Async/Await is just syntactic sugar. In this article, we will examine how it all works under the hood. To do that, we will attempt to recreate the async function from scratch!

Async Function

The key insight is to use Generators. Ultimately, we want to create an async function that takes in a generator. That way, we can nest yield statements that look like this:

Doesn’t this look uncannily like the await statements in getWeather()? In fact, await statements are just yield statements under the hood!

Thus, we need our async “polyfill” to instantiate the generator, recursively checking the value of next until the Promise resolves. When done is true, we can finally return the value of the Promise back to the calling yield statement.

Clever, right? 😉

Example in live code:

Conclusion

Async/Await enables us to write asynchronous code in a synchronous fashion, which produces cleaner and easier-to-understand logic. Under the hood, it’s just syntactic sugar using generators and yield statements to “pause” execution. In other words, async functions can “pull out” the value of a Promise even though it’s nested inside a callback function, giving us the ability to assign it to a variable! We hope this article gave you a better intuition behind this awesome feature coming to ES8! 😇

Russian translation by Alexey Vechkanov:

Reference: Async/Await Specification