JavaScript course menu
JavaScript async and await: Writing Asynchronous Code That Reads Like Synchronous Code
Learn async and await in JavaScript: async functions return promises, await pauses until a promise settles, error handling with try/catch/finally, sequential versus parallel awaits with Promise.all, top-level await, await in loops, and common mistakes.
On this page
async and await are a nicer way to work with promises. They let you write asynchronous code that reads top to bottom, like normal synchronous code, while still not blocking the page.
The same code, two ways#
This lesson uses a small simulated "request" with a timer:
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
// With .then()
function showUserThen() {
return wait(20, { name: "Asha" }).then((user) => {
console.log("then:", user.name);
});
}
// With async/await
async function showUserAwait() {
const user = await wait(20, { name: "Asha" });
console.log("await:", user.name);
}
await showUserThen();
await showUserAwait();then: Asha
await: Ashaasyncbefore a function makes it an async function.- Inside it,
await promisepauses that function until the promise settles, then gives you its value. - While it is paused, the rest of the program (and the page) keeps running.
(The last two lines use await at the top level of the file, which is allowed in modules; see below.)
async functions return promises#
An async function always returns a promise, even if you return a plain value:
async function getNumber() {
return 42;
}
const result = getNumber();
console.log(result instanceof Promise);
console.log(await result);true
42So callers either await it (inside another async function) or use .then().
Handling errors with try...catch#
When an awaited promise rejects, await throws the error, so you handle it with ordinary try...catch:
function getUser(id) {
return new Promise((resolve, reject) =>
setTimeout(() => (id === 1 ? resolve({ name: "Asha" }) : reject(new Error(`User ${id} not found`))), 20)
);
}
async function showUser(id) {
try {
const user = await getUser(id);
console.log("Hello,", user.name);
} catch (error) {
console.log("Could not load user:", error.message);
} finally {
console.log(`(request for user ${id} done)`);
}
}
await showUser(1);
await showUser(7);Hello, Asha
(request for user 1 done)
Could not load user: User 7 not found
(request for user 7 done)Errors from async code are handled exactly like synchronous ones, which is one of the biggest benefits of await.
Sequential versus parallel#
Each await waits before the next line starts. That is right when a step depends on the previous one, but wasteful when the steps are independent:
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
let start = Date.now();
const a = await wait(100, "profile");
const b = await wait(100, "orders");
console.log("sequential took about", Math.round((Date.now() - start) / 100) * 100, "ms");
start = Date.now();
const [c, d] = await Promise.all([wait(100, "profile"), wait(100, "orders")]);
console.log("parallel took about", Math.round((Date.now() - start) / 100) * 100, "ms");
console.log(a, b, c, d);sequential took about 200 ms
parallel took about 100 ms
profile orders profile ordersStart independent work together and await Promise.all([...]) the results.
Loops#
To process items one after another, use await inside a for...of loop:
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
for (const step of ["upload", "resize", "publish"]) {
const done = await wait(10, step);
console.log("done:", done);
}done: upload
done: resize
done: publishforEach does not wait for async callbacks: items.forEach(async (item) => { await ... }) starts them all at once and moves on immediately. Use for...of for one-by-one, or Promise.all(items.map(async (item) => ...)) for all-at-once.
Top-level await#
In modules (files loaded with type="module" in browsers, .mjs files or "type": "module" projects in Node.js), you can use await outside any function, as the examples on this page do. In classic scripts, wrap the code in an async function instead:
async function main() {
const data = await loadData();
console.log(data);
}
main();Common mistakes#
Practice#
Practice exercise
Using the wait helper, write an async function loadDashboard() that loads "user" (30 ms), then, in parallel, "orders" (40 ms) and "messages" (20 ms), and returns an object with all three. Print the result.
Show solution
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
async function loadDashboard() {
const user = await wait(30, "user");
const [orders, messages] = await Promise.all([wait(40, "orders"), wait(20, "messages")]);
return { user, orders, messages };
}
console.log(await loadDashboard());{ user: 'user', orders: 'orders', messages: 'messages' }Key takeaway#
Finished this lesson?