Async/Await in JavaScript
Learn to Write Cleaner Asynchronous Code.

You've met promises. You understand .then(). You've chained a few calls together and it mostly worked. But then you needed to do three async things in sequence, and you ended up with something like this:
getUser(userId)
.then(user => {
return getOrders(user.id);
})
.then(orders => {
return getProduct(orders[0].productId);
})
.then(product => {
console.log(product.name);
})
.catch(err => {
console.error(err);
});
It works. But it reads sideways. Each .then() is a new indentation, a new callback, a new mental context to hold. You can't easily share variables between steps without hoisting them outside the chain. Error handling applies to the whole chain but it's tacked on at the end, invisible until something breaks.
async/await was introduced in ES2017 to fix exactly this. Not by replacing promises by giving you a way to write promise-based code that looks and reads like regular synchronous code.
Syntactic sugar over promises
The first thing to understand is that async/await is not a new async system. It's a new syntax for working with promises. Under the hood, every async function returns a promise. Every await pauses execution and waits for a promise to resolve. No new engine magic just a cleaner way to write what you were already doing.
Think of it this way. Promises gave you a contract: "this will eventually have a value." async/await gives you a way to write code as if that value is already there, and JavaScript handles the waiting behind the scenes.
The async keyword
Putting async before a function declaration does one thing: it makes that function always return a promise. Even if you return a plain value, JavaScript automatically wraps it in a resolved promise.
async function greet() {
return "Hello!";
}
greet().then(msg => console.log(msg)); // "Hello!"
You returned a string. But because the function is async, the caller receives a promise that resolves to that string. That's the contract.
You can use async with any function style:
// function declaration
async function fetchData() { ... }
// arrow function
const fetchData = async () => { ... }
// method in an object
const api = {
async getUser() { ... }
};
The await keyword
await can only live inside an async function. It pauses that function's execution until the promise it's waiting on resolves then hands you the resolved value directly.
async function loadUser() {
const user = await getUser(1); // pauses here until resolved
console.log(user.name); // then continues
}
Without await, getUser(1) returns a promise object not the user. With await, you get the user directly, as if the function was synchronous. The pause is real but it's non-blocking: while this function is waiting, JavaScript keeps running everything else.
Here's what that promise chain from earlier looks like rewritten with async/await:
async function loadProductFromUser(userId) {
const user = await getUser(userId);
const orders = await getOrders(user.id);
const product = await getProduct(orders[0].productId);
console.log(product.name);
}
Top to bottom. Left to right. Each line waits for the previous one before moving on. No callbacks. No indentation staircase. It reads like a recipe: get the user, get their orders, get the first product, show its name.
Here's the execution flow when you call an async function:






