Skip to main content

Asynchronous JavaScript and Fetch

JavaScript runs on a single thread. Slow work (network, timers) is asynchronous: it is started, and its callback runs later via the event loop.

console.log("1");
setTimeout(() => console.log("3"), 0);
Promise.resolve().then(() => console.log("2"));
console.log("4");
// 1, 4, 2, 3 (microtasks run before timers)

Promises and async/await​

function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

async function run() {
console.log("start");
await wait(500);
console.log("done after 500ms");
}
run();

Fetch API​

async function loadUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`); // fetch does not throw on 404/500
}
return response.json();
}

try {
const user = await loadUser(42);
console.log(user.name);
} catch (err) {
console.error("Failed:", err.message);
}

Sending JSON​

await fetch("/api/notes", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: "Learn async" }),
});

Parallel requests​

const [users, posts] = await Promise.all([
fetch("/api/users").then((r) => r.json()),
fetch("/api/posts").then((r) => r.json()),
]);

Promise.all fails fast; Promise.allSettled waits for every result.

Good practices​

  • Always handle errors and show loading and error states in the UI.
  • Cancel stale requests with AbortController.
  • Do not trust the response shape: validate it.
  • Remember CORS: the server must allow cross-origin browser requests.