JavaScript Async Await and Fetch
JavaScript async await is a cleaner way to work with promises. An async function always returns a promise, and inside it, await pauses that function until a promise settles, without blocking the rest of the page.
Writing an async Function
Each await waits for the promise before the next line runs, so the code reads top to bottom like synchronous code.
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
async function makeTea() {
console.log("Boiling water...");
await wait(300);
console.log("Steeping...");
await wait(300);
return "Tea is ready";
}
makeTea().then(console.log);Boiling water... Steeping... Tea is ready
Fetching JSON from an API
fetch(url) returns a promise for a Response. Check response.ok, then call response.json() to parse the body. This example calls a free public test API.
async function getTodo(id) {
const res = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
getTodo(1)
.then((todo) => console.log(todo.id, todo.title, todo.completed))
.catch((e) => console.log("Request failed:", e.message));1 delectus aut autem false
Error Handling with try...catch
fetch only rejects on network failure. A 404 or 500 still resolves, so always check res.ok. Wrap awaits in try...catch to handle both cases.
async function load(url) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error("Status " + res.status);
const data = await res.json();
console.log("Loaded", data.length, "items");
} catch (err) {
console.log("Error:", err.message);
}
}
load("https://jsonplaceholder.typicode.com/users");
load("https://jsonplaceholder.typicode.com/nothing-here");Error: Status 404 Loaded 10 items
Sending Data with POST
Pass an options object with method, headers and a JSON string body. Top-level await like this works in modules.
const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "Hello", body: "First post", userId: 1 }),
});
const created = await res.json();
console.log(created.id);101
Sequential vs Parallel
Awaiting one call after another runs them in sequence. If the calls do not depend on each other, start them together with Promise.all to save time.
const delay = (v) => new Promise((r) => setTimeout(() => r(v), 300));
async function main() {
const t = performance.now();
const [a, b, c] = await Promise.all([delay(1), delay(2), delay(3)]);
console.log(a + b + c, "in about", Math.round((performance.now() - t) / 100) * 100, "ms");
}
main();6 in about 300 ms
Frequently asked questions
Can I use await outside an async function?
Only at the top level of an ES module (top-level await). In regular scripts and normal functions, await must be inside an async function.
Why does fetch not throw on a 404 error?
fetch treats any HTTP response as success. Check response.ok or response.status yourself and throw if needed.
Is async await faster than promises?
No. It is the same mechanism with nicer syntax. Speed depends on whether you run tasks sequentially or in parallel.