Async / Await in JavaScript
Async/await is a way to write code that handles things that take time — like fetching data from a server, reading a file, or waiting for a timer — without your whole program freezing up.
Think of it like ordering food at a restaurant. You place your order and wait. But while the kitchen is cooking, you don't just stand there frozen — you talk to your friends, check your phone, and carry on. When the food is ready, the waiter brings it to you. That is exactly how async/await works.
Before async/await existed, developers used Promises with .then() and .catch(). Async/await is built on top of Promises — it is just a cleaner and easier way to write the same code.
// Without async/await (using .then)
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then(response => response.json())
.then(data => console.log(data.title))
.catch(err => console.error(err));
// With async/await (same thing, easier to read)
async function getPost() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await response.json();
console.log(data.title);
} catch (err) {
console.error(err);
}
}
getPost();
Both examples do the same thing. But the async/await version reads top to bottom, just like normal code. No nested callbacks. No chain of .then(). Much easier to follow.
When you put async in front of a function, two things happen:
- The function always returns a Promise — even if you return a plain value like a string or number
- You are now allowed to use the await keyword inside it
async function greet() {
return "Hello!";
}
// Even though we returned a plain string,
// the function returns a Promise
greet().then(message => console.log(message)); // Hello!
You can use async with any type of function:
// Regular function
async function greet() {
return "Hello!";
}
// Arrow function
const greet = async () => {
return "Hello!";
};
// Object method
const user = {
getName: async function() {
return "Alice";
}
};
// Class method
class UserService {
async getUser() {
return { name: "Alice" };
}
}
await tells JavaScript: "Stop here and wait for this Promise to finish before moving to the next line." But here is the important part — it only pauses inside the async function. The rest of your app keeps running normally.
Think of it like pressing pause on a video. The video pauses, but everyone else in the room can still move around. Other parts of your code are not blocked.
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function run() {
console.log("Start");
await wait(2000); // pauses here for 2 seconds
console.log("Done after 2 seconds");
}
run();
console.log("This runs immediately — not blocked");
You can only use await inside an async function. Trying to use it outside will cause a syntax error.
async function loadData() {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await response.json();
console.log(data.title); // delectus aut autem
console.log(data.completed); // false
}
loadData();
When a Promise rejects (something goes wrong), await throws an error. To handle it, wrap your await calls inside a try/catch block.
Think of try/catch like a safety net. You try to do something that might fail — if it does, the catch block runs, and your app does not crash.
async function getUser(id) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
if (!response.ok) {
throw new Error("User not found");
}
const user = await response.json();
console.log(user.name);
} catch (error) {
console.error("Something went wrong:", error.message);
}
}
getUser(1); // Leanne Graham
getUser(999); // Something went wrong: User not found
You can also add a finally block for code that should always run, no matter what happens — like hiding a loading spinner:
async function loadData() {
try {
const res = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const data = await res.json();
console.log(data.title);
} catch (err) {
console.error("Failed to load:", err.message);
} finally {
console.log("Loading finished"); // always runs
}
}
loadData();
Async/await does not replace Promises — it is built on top of them. Under the hood, an async function returns a Promise. await just pauses execution until that Promise resolves. You are writing the same logic, just in a cleaner way.
// Using Promises (.then chains)
function getPost() {
return fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(res => res.json())
.then(data => {
console.log(data.title);
})
.catch(err => console.error(err));
}
// Same thing using async/await
async function getPost() {
try {
const res = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const data = await res.json();
console.log(data.title);
} catch (err) {
console.error(err);
}
}
Both versions produce the same result. Use async/await when your code has multiple steps that depend on each other. Use Promise.all() when you want multiple things to happen at the same time:
// Running multiple tasks in parallel
async function loadDashboard() {
const [users, posts] = await Promise.all([
fetch("https://jsonplaceholder.typicode.com/users").then(r => r.json()),
fetch("https://jsonplaceholder.typicode.com/posts").then(r => r.json())
]);
console.log("Users:", users.length); // 10
console.log("Posts:", posts.length); // 100
}
loadDashboard();
- async makes a function always return a Promise
- await pauses execution inside an async function until a Promise resolves
- The rest of your code outside the async function keeps running — it is not blocked
- Use try/catch to handle errors inside async functions
- Add a finally block for cleanup code that always needs to run
- Async/await is built on top of Promises — they are not competing concepts
- Use Promise.all() to run multiple independent tasks at the same time
- await can only be used inside an async function
What's next? Now that you know how async and await work, let's use them with real HTTP requests in the next tutorial.
- What does the async keyword do to a function?
- What does await do, and where can you use it?
- What is the difference between async/await and Promises?
- What happens if you use await outside an async function?
- How do you handle errors in async/await?
- What is the difference between using try/catch vs .catch() with async functions?
- How would you run two API calls at the same time using async/await?
- Does await block the main thread?
- Can you use async/await with forEach? What is the problem if you do?
- What does an async function return if you return a plain value like a string?