Introduction
async/await is syntactic sugar built on top of Promises that lets you write asynchronous code in a linear, synchronous-looking style. An async function always returns a Promise. Inside an async function, the await keyword pauses execution of that function (without blocking the rest of the program) until the awaited Promise settles, then resumes with the fulfilled value or throws the rejection reason as an exception.
Cricket analogy: await is like a non-striker batsman waiting at the crease for the umpire's signal before running — the rest of the ground keeps functioning, but that specific play pauses until the delivery is resolved.
Syntax
async function getData() {
try {
const value = await somePromiseReturningFunction();
console.log(value);
return value;
} catch (err) {
console.error('Caught error:', err.message);
throw err; // optional: re-throw for the caller to handle
}
}
getData().then((v) => console.log('Resolved with', v));Explanation
Because await only works with Promises (or thenables), and an async function's return value is automatically wrapped in a Promise, async/await is fully interoperable with existing Promise-based code — it does not replace Promises, it just provides better ergonomics on top of them. Error handling uses standard try/catch instead of .catch(): if an awaited Promise rejects, the rejection is thrown as an exception at the await expression, which a surrounding try/catch can intercept. Without a try/catch, the rejection instead causes the async function's returned Promise to reject.
Cricket analogy: Using try/catch around await is like a fielder wearing a helmet at short leg — if the ball takes an unexpected edge, the helmet (catch block) absorbs the shock instead of the mishap ending the innings entirely.
Example
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) resolve({ id, name: 'Alice' });
else reject(new Error('Invalid id'));
}, 300);
});
}
function fetchOrders(userId) {
return new Promise((resolve) => setTimeout(() => resolve(['order1', 'order2']), 300));
}
async function loadDashboard(id) {
try {
const user = await fetchUser(id);
const orders = await fetchOrders(user.id);
console.log(`${user.name}'s orders:`, orders);
} catch (err) {
console.error('Failed to load dashboard:', err.message);
}
}
loadDashboard(42);
// Running independent awaits concurrently
async function loadBoth() {
const [u1, u2] = await Promise.all([fetchUser(1), fetchUser(2)]);
console.log(u1, u2);
}Output
loadDashboard(42) logs "Alice's orders: [ 'order1', 'order2' ]" after roughly 600ms (300ms for fetchUser, then 300ms for fetchOrders sequentially, because the second await waits for the first to finish). If id were invalid, fetchUser's rejection would be caught by the catch block, logging 'Failed to load dashboard: Invalid id'. In loadBoth(), wrapping independent awaits in Promise.all runs them concurrently instead of sequentially, finishing in ~300ms total instead of ~600ms.
Cricket analogy: Batting first then bowling second sequentially in an exhibition takes far longer than running two simultaneous net sessions on separate pitches — Promise.all is like using two nets at once to finish training faster.
Key Takeaways
- async functions always return a Promise, even if you return a plain value.
- await pauses only the current async function, not the whole program (the event loop keeps running).
- await can only be used with a value that is a Promise (or thenable) meaningfully, and only inside async functions (or top-level modules).
- Use try/catch around await to handle rejections; unhandled rejections propagate as a rejected Promise from the async function.
- Sequential awaits run one after another; use Promise.all to run independent async operations concurrently for better performance.
Practice what you learned
1. What does an async function always return?
2. What happens when you await a rejected Promise inside a try block?
3. In the loadDashboard example, why does it take about 600ms instead of 300ms?
4. How can you run two independent async operations concurrently with async/await?
Was this page helpful?
You May Also Like
Promises in Node.js
Understanding the Promise object and its pending, fulfilled, and rejected states for cleaner async code.
Callbacks in Node.js
How Node.js uses callback functions to handle asynchronous operations without blocking the main thread.
Error Handling in Express
Master synchronous and asynchronous error handling in Express using 4-argument error middleware.
Common Node.js Interview Questions
A curated set of frequently asked Node.js interview questions with clear, technically accurate answers.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics