Skip to main content
Web MobileIntermediate14 min readUpdated March 2025

Async JavaScript: Promises & async/await

Network requests, timers and file access are asynchronous. Understand the event loop, Promises and async/await to write non-blocking code without callback hell.

The Event Loop in One Minute

JavaScript runs on a single thread. Long operations such as network calls are handed to the environment, and their callbacks are queued to run when the call stack is empty. This keeps the page responsive instead of freezing while waiting.

Promises

A Promise represents a value that will exist in the future. It is either pending, fulfilled or rejected. You chain results with `.then()` and handle errors with `.catch()`.

javascript
const wait = (ms) =>
  new Promise((resolve) => setTimeout(resolve, ms));

wait(500)
  .then(() => fetch("https://api.github.com/users/octocat"))
  .then((res) => res.json())
  .then((user) => console.log(user.login))
  .catch((err) => console.error("Failed:", err))
  .finally(() => console.log("done"));

async / await

`async/await` is syntax built on Promises that reads like synchronous code. Always wrap awaited calls in `try/catch`, and remember that `fetch` does not reject on HTTP error statuses, so check `res.ok`.

javascript
async function getUser(username) {
  try {
    const res = await fetch(`https://api.github.com/users/${username}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.json();
  } catch (error) {
    console.error("Could not load user:", error.message);
    return null;
  }
}

const user = await getUser("octocat");
console.log(user?.public_repos);

Running Tasks in Parallel

Awaiting calls one after another is slow when they are independent. Start them together with `Promise.all` (fail fast) or `Promise.allSettled` (collect every result):

javascript
const users = ["octocat", "torvalds", "gaearon"];

const results = await Promise.allSettled(
  users.map((u) => fetch(`https://api.github.com/users/${u}`).then((r) => r.json()))
);

results.forEach((r, i) => {
  if (r.status === "fulfilled") console.log(users[i], r.value.followers);
  else console.warn(users[i], "failed");
});

Key Takeaways

  • JavaScript is single-threaded; async work is queued through the event loop.
  • Promises model future values; async/await makes them read linearly.
  • Always handle errors and check res.ok when using fetch.
  • Use Promise.all or allSettled to run independent tasks in parallel.

Contact Us

Have a question or feedback? Fill out the form below or reach us directly at support@nvaitraining.com