Quick answer: JavaScript async/await is the readable way to consume a promise — a value that will arrive later. Use await inside an async function to pause until the value resolves, wrap risky calls in try…catch, and run independent work in parallel with Promise.all.
Table of Contents
What a promise is
A promise has three states: pending, fulfilled or rejected. It settles exactly once. Before promises, asynchronous code used callbacks, which nested badly and lost their error handling. A promise gives you a value you can chain.
fetch("/api/user")
.then(res => res.json())
.then(user => console.log(user.name))
.catch(err => console.error("Failed:", err))
.finally(() => hideSpinner());
JavaScript async/await in practice
The same code reads like ordinary sequential statements with async and await. An async function always returns a promise, and await pauses inside it until the awaited promise settles.
async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const user = await res.json();
return user;
} catch (err) {
console.error("Failed:", err);
return null;
} finally {
hideSpinner();
}
}
Two habits make this robust. First, check res.ok — fetch does not reject on a 404 or 500, so a failed request looks successful unless you test it. Second, use try…catch rather than leaving a .catch() dangling, so the failure path sits next to the code that can fail.
Sequential versus parallel with JavaScript async/await
A common performance mistake is awaiting tasks one after another when they do not depend on each other. Three one-second requests take three seconds if serialised, but one second if run together.
// slow: each waits for the previous
const a = await load("a");
const b = await load("b");
// fast: start both, then await the pair
const [a2, b2] = await Promise.all([load("a"), load("b")]);
| Method | Behaviour |
|---|---|
Promise.all | Waits for all; rejects if any rejects |
Promise.allSettled | Waits for all; never rejects, reports each result |
Promise.race | Settles with the first to finish |
Promise.any | Resolves on the first success |
Use all when every result is required and a single failure should abort. Use allSettled when you want partial results — for example, a dashboard where one widget may fail but the others should still render.
JavaScript async/await error handling that holds up
async function loadDashboard() {
const results = await Promise.allSettled([loadUser(), loadOrders()]);
results.forEach(r => {
if (r.status === "fulfilled") render(r.value);
else logFailure(r.reason);
});
}
An await that rejects inside a function with no try…catch becomes an unhandled rejection. Add a global listener during development so those never pass silently:
window.addEventListener("unhandledrejection", e => {
console.error("Unhandled:", e.reason);
});
Creating your own promise
You rarely need to construct one, because most APIs are already promise-based. When you must wrap a callback API, do it once in a small helper rather than everywhere it is used.
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await wait(500); // pause half a second inside an async function
Next: the Fetch API, where promises meet the network. To learn async JavaScript with a mentor, Ampersand Academy teaches web development one-to-one.
Frequently asked questions
What is a promise in JavaScript?
An object representing a value that will be available later. It is pending, then either fulfilled with a value or rejected with an error, and it settles only once.
What is the difference between Promise.all and Promise.allSettled?
Promise.all rejects as soon as any input rejects. Promise.allSettled waits for every input and reports each outcome, so you can render partial results.
Why does my fetch request not throw on a 404?
fetch only rejects on a network failure, not on an HTTP error status. Check response.ok and throw yourself when it is false.
Can I use await outside an async function?
Yes, at the top level of a module in current browsers and Node, top-level await is supported. Inside a regular function you still need the async keyword.
How do I run async tasks in parallel?
Start them first and await them together, for example with Promise.all on an array of promises, instead of awaiting each one in sequence.

