JavaScript 101: async/await
Disclaimer: This material was taught in The Modern Javascript Bootcamp Course by Colt Steele and Stephen Grider, which I highly recommend.
Async functions are features of JavaScript since ECMAScript 2017 that do not, unfortunately, work on Internet Explorer. According to MDN’s JavaScript async function page, “The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.” In other words, you can combine async and await to write asynchronous code, which will properly replace .then() promise chains. Specifically, the async keyword is a way of wrapping a function in a promise, while the await keyword will pause the execution of the function while it waits for the asynchronous operation to be resolved. This article will expound upon the basics of async and await.
In general, functions declared with the async keyword always return a promise. Thus, if the async function returns a value, it will still return a promise, but the promise will be resolved with that value. Below is an example of how the return would change with the async keyword:

Running the function without the async keyword, stringGreeting(), returns a String, “Hello”. If we put the async declaration in front of the function, asyncStringGreeting() in this case, we return Promise(<resolved>: “Hello”), which is a promise that is resolved with the value of “Hello”.
So how can we take that promise returned by asyncStringGreeting() to print a string that includes “Hello”? Add a .then() to asyncStringGreeting():

The .then() resolves the promise of asyncStringGreeting(). “Hello” is passed in as the input value into this .then(), which is used to print “Promise resolved with Hello”.
In general, promises are either resolved or rejected, which can also be applied to functions that use the async keyword. To reject a promise, we can use conditional logic followed by the throw keyword. If the function throws an exception, the promise will be rejected:

Both of the arguments in add(4, 5) are typeof numbers so they would skip the if statement inside async function add(a,b) and return 4 + 5, which equals 9. Since an async function always returns a promise, the add(4, 5) invocation returns a promise that is resolved with the value of 9.
The first argument in add(‘c’, 5) is not a typeof number, so the if statement is executed. The add(‘c’, 5) invocation returns a promise that is rejected along with the error message after the throw keyword, “One of a or b is not a number.”
Instead of returning a rejected promise, we can throw the error message by using a .catch():

The function now follows the resolve or reject formula of a promise. For add(4, 5), the .then() receives a resolved promise with a value of 9 as an input, which it then console.log()s to print “Promise resolved totaling 9”.
For add(‘California’, ‘New York’), the .catch() receives the rejected promise with a value of ‘One of a or b is not a number.’ as an input, which it then console.log()s to print “Promise rejected. One of a or b is not a number.”
Overall, if a and b are both numbers, the function will resolve, adding the two numbers and executing in the .then() with the inputted value of a+b. If one of a or b is not a number, the function will reject the promise, send the String written after the throw keyword as the error inputted into .catch(error), which is then used to print the error message.
Now, let’s discuss the await keyword, which can only be used inside an async function. As mentioned earlier, the await keyword will pause the execution of the function while it waits for the asynchronous operation to be resolved.
In its utilization, the await keyword can be used to replace a .then() chain. Let’s say we use axios.get() to retrieve a promise that contains user information:

axios.get() returns a promise on which you can chain a .then() to print the user data. You can avoid this axios.get().then() chain by using the await keyword. Using await signifies that we will wait for axios.get() to be resolved before moving on to the console.log() below it. The results of response.data in the axios.get().then() chain is the same as the results of result.data in getUserData(). However, if we did not use the await keyword, console.log(result.data) would print undefined because the promise is not yet resolved when console.log() is run.
In the end, async and await ultimately use promises and function the same as adding a .then() chain or chains to a promise. Async and await are, more often than not, easier to read as there is a straightforward path to see the order in which its asynchronous functions are running.