JavaScript Async/Await
Async/await is a modern syntax for working with asynchronous operations. It is built on top of Promises and makes asynchronous code look and behave like synchronous code — far easier to read, write, and debug compared to promise chains.
Real-life analogy: Async/await is like placing an online order and waiting at the delivery counter. The process runs in the background; the work only continues once the order arrives. No nested callbacks, no chain of .then() calls — just a straight-line reading flow.
The async Keyword
The async keyword is placed before a function definition to mark it as asynchronous. An async function always returns a Promise — even if a plain value is returned.
async function greet() {
return "Hello!";
}
greet().then(msg => console.log(msg)); // Hello!
// The above is equivalent to:
function greet() {
return Promise.resolve("Hello!");
}The await Keyword
The await keyword can only be used inside an async function. It pauses execution of the async function until the Promise is settled, then resumes with the resolved value.
function fetchUser() {
return new Promise(resolve => {
setTimeout(() => {
resolve({ name: "Priti", role: "admin" });
}, 2000);
});
}
async function displayUser() {
console.log("Fetching user...");
let user = await fetchUser(); // Wait until fetchUser() resolves
console.log("User:", user.name, "| Role:", user.role);
console.log("Done.");
}
displayUser();
// Fetching user...
// (2 second pause)
// User: Priti | Role: admin
// Done.Error Handling with try/catch
Async/await uses try...catch blocks for error handling — the same mechanism used for synchronous code, making it consistent and familiar.
function fetchData(shouldFail) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject("Data not found!");
} else {
resolve({ id: 1, value: "Success data" });
}
}, 1000);
});
}
async function loadData() {
try {
let data = await fetchData(false);
console.log("Loaded:", data.value);
} catch (error) {
console.log("Error caught:", error);
} finally {
console.log("Loading complete.");
}
}
loadData();
// Loaded: Success data
// Loading complete.Sequential Async Operations
Async/await makes sequential operations (each depending on the previous) straightforward to write.
function getUser(id) {
return new Promise(resolve =>
setTimeout(() => resolve({ id, name: "Kiran" }), 500)
);
}
function getOrders(userId) {
return new Promise(resolve =>
setTimeout(() => resolve(["Order #101", "Order #102"]), 500)
);
}
function getOrderDetail(order) {
return new Promise(resolve =>
setTimeout(() => resolve({ order, status: "Delivered" }), 500)
);
}
async function showUserInfo(userId) {
let user = await getUser(userId);
let orders = await getOrders(user.id);
let detail = await getOrderDetail(orders[0]);
console.log("User:", user.name);
console.log("First Order:", detail.order);
console.log("Status:", detail.status);
}
showUserInfo(5);
// User: Kiran
// First Order: Order #101
// Status: DeliveredParallel Async Operations with Promise.all
When tasks do not depend on each other, running them in parallel with Promise.all() is faster than running them sequentially.
async function fetchReports() {
let [sales, inventory, users] = await Promise.all([
fetchSalesData(),
fetchInventory(),
fetchUsers()
]);
console.log("Sales:", sales);
console.log("Inventory:", inventory);
console.log("Users:", users);
}
// All three requests run simultaneously — much faster!Async in Arrow Functions
const fetchProduct = async (id) => {
try {
let response = await fetch(`https://api.example.com/products/${id}`);
let product = await response.json();
return product;
} catch (error) {
console.log("Fetch failed:", error.message);
}
};
fetchProduct(42).then(p => console.log(p));Handling Multiple Errors Independently
async function processData() {
let userData, orderData;
try {
userData = await fetchUser(1);
} catch {
userData = { name: "Guest" }; // Fallback
console.log("User fetch failed, using guest.");
}
try {
orderData = await fetchOrders(userData.id);
} catch {
orderData = [];
console.log("Orders unavailable.");
}
console.log("Processing:", userData.name);
console.log("Orders found:", orderData.length);
}Awaiting Non-Promise Values
Using await on a non-promise value simply returns it immediately without any delay.
async function example() {
let x = await 42; // Works fine — same as: let x = 42
console.log(x); // 42
}async/await vs .then()/.catch() Comparison
| Feature | Promise Chain | async/await |
|---|---|---|
| Readability | Moderate (chain-based) | High (linear, like sync code) |
| Error handling | .catch() | try/catch |
| Debugging | Harder | Easier (step through like sync) |
| Parallel tasks | Promise.all() | await Promise.all() |
| Compatibility | ES6+ | ES2017+ |
Common Mistakes with async/await
Mistake 1 — Forgetting await
async function getData() {
let data = fetchUser(); // Missing await! "data" is a Promise, not the value
console.log(data); // [object Promise] — wrong!
}
// Correct:
async function getData() {
let data = await fetchUser(); // Now data is the resolved value
console.log(data);
}Mistake 2 — Using await Outside async Function
// This throws a SyntaxError:
// let result = await fetchUser();
// Must be inside an async function:
async function run() {
let result = await fetchUser();
console.log(result);
}Mistake 3 — Sequential When It Should Be Parallel
// Slow: runs one at a time (3 seconds total)
async function slow() {
let a = await wait(1000); // Wait 1s
let b = await wait(1000); // Then wait 1s
let c = await wait(1000); // Then wait 1s
}
// Fast: runs all at once (1 second total)
async function fast() {
let [a, b, c] = await Promise.all([wait(1000), wait(1000), wait(1000)]);
}Practical Example — Async Data Loader
function simulateAPI(data, delay, fail = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
fail ? reject("API Error!") : resolve(data);
}, delay);
});
}
async function loadDashboard() {
console.log("Loading dashboard...");
try {
let [profile, stats] = await Promise.all([
simulateAPI({ name: "Ananya", tier: "Premium" }, 800),
simulateAPI({ visits: 1240, orders: 58 }, 600)
]);
console.log(`Welcome, ${profile.name}! (${profile.tier})`);
console.log(`Visits: ${stats.visits} | Orders: ${stats.orders}`);
} catch (error) {
console.log("Dashboard load failed:", error);
}
}
loadDashboard();Key Points to Remember
- The
asynckeyword makes a function always return a Promise - The
awaitkeyword pauses the async function until the Promise settles - Always use
try/catchinside async functions to handle errors awaitcan only be used inside anasyncfunction- For independent operations, use
await Promise.all()to run them in parallel - Async/await is syntactic sugar over Promises — both achieve the same result
- Forgetting
awaitis a common bug — the variable will hold a Promise, not the value
