async/await is how almost all modern asynchronous JavaScript is written.
An async function always returns a promise. Inside it, `await` pauses the function until a promise settles, then gives you the fulfilled value — or throws the rejection reason:
async function showOrders(id) {
try {
const user = await loadUser(id);
const orders = await loadOrders(user.id);
render(orders);
} catch (err) {
showError(err);
}
}It's still promises underneath: the return value becomes the promise's value and a throw rejects it. Pausing doesn't block the thread; other code runs while the function waits. await also works at the top level of ES modules.
The most important performance habit: don't serialise independent work. Each await waits before the next line starts, so
const a = await getA();
const b = await getB();takes the sum of both durations. If they don't depend on each other, start both and await together: const [a, b] = await Promise.all([getA(), getB()]).
Loops need care:
for...ofwithawaitinside processes items one after another — correct when order or rate limits matter.array.forEach(async ...)does not wait;forEachignores the returned promises, so the code after it runs immediately and errors go unhandled.- For concurrent processing,
await Promise.all(items.map(async item => ...)).
Calling an async function without await (or .catch) "fires and forgets" it; any error becomes an unhandled rejection. Linters can flag floating promises. Inside try, write return await p if you want that catch to handle p's rejection.
Example
const sleep = (ms, v) => new Promise(r => setTimeout(() => r(v), ms));
async function getUser(id) { await sleep(50); return { id, name: "Ada" }; }
async function getPosts(id) { await sleep(50); return ["hello", "world"]; }
async function getFails() { await sleep(10); throw new Error("not found"); }
async function main() {
let t = Date.now();
const user = await getUser(1);
const posts = await getPosts(1); // waits for getUser first
console.log("sequential ~100ms:", Date.now() - t >= 100);
t = Date.now();
const [u2, p2] = await Promise.all([getUser(1), getPosts(1)]);
console.log("parallel ~50ms:", Date.now() - t < 100); // true
try {
await getFails();
} catch (err) {
console.log("caught:", err.message); // caught: not found
}
return `${user.name} has ${posts.length} posts`;
}
main().then(console.log); // Ada has 2 postsRemember
- An async function returns a promise;
awaitunwraps one or throws - Start independent work together and
await Promise.all([...]) forEachdoesn't await — usefor...of(sequential) orPromise.all+map
Watch out for
items.forEach(async item => await save(item))and assuming everything is saved afterwards.