Promises are powerful, but long chains of .then() calls can become hard to follow. async/await lets you write asynchronous code that reads like normal code from top to bottom, while the page still never freezes. It's how most modern JavaScript loads and sends data.
async/await isn't a replacement for promises; it's a nicer way of using them. Everything you learned in the Promises lesson still applies underneath. The difference is that your code describes the steps in order, which makes it easier to read, debug and change.
async functions
Put async before a function and it always returns a Promise:
async function getNumber() {
return 42;
}
getNumber().then(n => console.log(n)); // 42It works with arrow functions too: const load = async () => { … };. Even if you return a plain value, the caller receives a promise for that value.
await: waiting for a result
Inside an async function, await pauses that function until a promise settles, then gives you its value:
async function showUser() {
const user = await fetchUser(); // wait here…
console.log(user.name); // …then carry on
}Only the async function pauses. The rest of the page keeps working, so buttons still respond and animations keep running.
Forgetting await is a common mistake. Without it, the variable holds the promise itself rather than the result, and you'll see Promise { <pending> } in the console.
Handling errors with try...catch
If an awaited promise is rejected, await throws an error. Handle it with the familiar try...catch:
async function load() {
try {
const data = await getData();
show(data);
} catch (error) {
showError(error.message);
} finally {
hideSpinner();
}
}This is the same structure you learned in Errors, which is one of the main advantages of async/await.
Running tasks in sequence or in parallel
Using await on one task after another runs them in sequence, with each waiting for the previous one. If the tasks don't depend on each other, start them together:
const [user, posts] = await Promise.all([getUser(), getPosts()]);Two requests that each take half a second finish in about half a second together, instead of a full second one after the other.
JavaScript async/await Syntax
async/await
async function name() {
try {
const value = await somePromise;
return value;
} catch (error) {
// handle
}
}
const arrow = async () => { await thing(); };JavaScript async/await Summary
- An
asyncfunction always returns a promise. awaitpauses only the async function until a promise settles.- Forgetting
awaitleaves you with a promise instead of a value. - Use
try...catch...finallyto handle errors from awaited promises. - Use
Promise.allto run independent tasks in parallel.
JavaScript async/await Examples
Each example runs in the page. Edit the code and press Run to see what changes.
then vs await
Both functions do the same thing. Which is easier to read?
const wait = (ms, v) => new Promise(r => setTimeout(() => r(v), ms));
function withThen() {
return wait(300, "🥚")
.then(egg => wait(300, egg + "🍳"))
.then(meal => console.log("then:", meal));
}
async function withAwait() {
const egg = await wait(300, "🥚");
const meal = await wait(300, egg + "🍳");
console.log("await:", meal);
}
withThen();
withAwait();Line-by-line explanation
- Lines 3 to 7A promise chain.
- Lines 9 to 13The same steps with
await, which read from top to bottom.
Handling failure
Run it a few times.
function getWeather() {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.4
? resolve({ temp: 19, sky: "☀️" })
: reject(new Error("Weather service is down"));
}, 400);
});
}
async function showWeather() {
console.log("Loading…");
try {
const w = await getWeather();
console.log(`Today: ${w.temp}°C ${w.sky}`);
} catch (error) {
console.log("Sorry:", error.message);
} finally {
console.log("Done loading");
}
}
showWeather();Line-by-line explanation
- Lines 2 to 8A promise that randomly succeeds or fails.
- Line 14
awaitgives the value, or throws if the promise was rejected. - Lines 16 to 17
catchhandles the failure. - Lines 18 to 20
finallyalways runs.
Sequential vs parallel timing
const task = (name, ms) => new Promise(r => setTimeout(() => r(name), ms));
async function sequential() {
const t = Date.now();
await task("A", 400);
await task("B", 400);
console.log("Sequential:", Date.now() - t >= 790 ? "~800ms" : "?");
}
async function parallel() {
const t = Date.now();
await Promise.all([task("A", 400), task("B", 400)]);
console.log("Parallel:", Date.now() - t < 600 ? "~400ms" : "?");
}
sequential();
parallel();Line-by-line explanation
- Lines 3 to 8Each
awaitwaits for the previous task, so it takes 800ms in total. - Lines 10 to 14Both tasks start together, taking about 400ms.
Common JavaScript async/await Mistakes
await outside an async function
function load() {
const data = await getData(); // SyntaxError
}async function load() {
const data = await getData();
}Forgetting await
async function show() {
const user = getUser(); // a Promise!
console.log(user.name); // undefined
}async function show() {
const user = await getUser();
console.log(user.name);
}Awaiting in a loop when you don't need to
for (const id of ids) {
results.push(await getItem(id)); // one at a time
}const results = await Promise.all(ids.map(id => getItem(id)));When requests don't depend on each other, run them in parallel.
JavaScript async/await in Real Projects
async/await in practice
Loading a page's data, submitting a form to a server, uploading a photo and reading from a database in Node.js are almost always written with async/await today, usually wrapped in try...catch with a loading state shown on the page.
JavaScript async/await Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Make the function async and wait for the data.
function start() {
const data = loadData();
console.log(data);
}
await only works inside async functions.What does an async function always return?
return 5 becomes a Promise that resolves to 5.In what order are the letters logged?
async function run() {
console.log("B");
await null;
console.log("D");
}
console.log("A");
run();
console.log("C");getTotal should wait for both prices and log Total: 30. Fix it.
const price = (n) => new Promise(r => setTimeout(() => r(n), 100));
function getTotal() {
const a = price(10);
const b = price(20);
console.log("Total:", a + b);
}
getTotal();- Logs "Total: 30"
- getTotal is async
JavaScript async/await Quiz
3 questions to check what you learned.
Where can you use
awaitin a normal (non-module) script?- Only inside async functions
- Anywhere
- Only in loops
- Only with fetch
How do you handle errors from an awaited promise?
- .error()
- if (await)
- They can't be handled
- try…catch
How do you run two independent awaits at the same time?
await Promise.all([a(), b()])- await twice in a row
await a() && await b()- It's impossible
JavaScript async/await Challenge
Load a dashboard
Using the fake api functions:
- Write
async function loadDashboard()that awaitsgetUser()andgetTasks()in parallel withPromise.all - Log
Hi Rae, you have 3 tasks - Return the number of tasks
const api = (value, ms) => new Promise(r => setTimeout(() => r(value), ms));
const getUser = () => api({ name: "Rae" }, 200);
const getTasks = () => api(["Email", "Gym", "Read"], 300);
- loadDashboard resolves to 3
- Logs the greeting
- Uses Promise.all