AniUI Academy

Asynchronous JavaScript

Understand promises, async and await, and error handling with try/catch so you can fetch real data from an API without freezing the page or swallowing failures.

12 min read

Everything so far ran instantly, top to bottom. Real applications spend most of their time waiting — for an API, a file, a timer. JavaScript handles waiting without freezing the page, and this lesson is about how.

The problem asynchrony solves

JavaScript runs your code on a single thread. If fetching data blocked that thread, the entire page would freeze until the response arrived — no scrolling, no clicking, nothing. So slow operations are started, set aside, and picked up again when they finish.

This is why the ordering below surprises people:

console.log("first");
 
setTimeout(() => console.log("second"), 0);
 
console.log("third");
 
// first
// third
// second

Even with a zero-millisecond delay, the callback waits until the current work finishes. Anything asynchronous goes to the back of the queue.

Promises

A promise represents a value you do not have yet. It is in one of three states: pending, fulfilled, or rejected.

You will mostly consume promises rather than create them, but seeing one built makes the rest clearer:

const wait = (ms) =>
  new Promise((resolve) => setTimeout(resolve, ms));
 
wait(1000).then(() => console.log("a second later"));

The .then() chain works, but nests badly once you have several steps in a row. That is why async/await exists.

async and await

Mark a function async and you can await any promise inside it. The code reads top to bottom like normal, while still not blocking the page.

async function loadUser(id) {
  const response = await fetch("https://api.example.com/users/" + id);
  const user = await response.json();
  return user;
}

Two rules to internalise:

  • await only works inside an async function.
  • An async function always returns a promise, even when you return a plain value.

That second rule catches everyone at least once:

async function getName() {
  return "Anish";
}
 
console.log(getName());        // Promise { 'Anish' } — not the string
console.log(await getName());  // "Anish"

Handling failure

Networks fail. An await that rejects throws, so wrap it in try/catch:

async function loadUser(id) {
  try {
    const response = await fetch("https://api.example.com/users/" + id);
 
    if (!response.ok) {
      throw new Error("Request failed with status " + response.status);
    }
 
    return await response.json();
  } catch (error) {
    console.error("Could not load user:", error.message);
    return null;
  }
}

Note the response.ok check. This is the single most common fetch mistake: fetch does not reject on 404 or 500. It only rejects when the request itself could not be made at all, like a dropped connection. A 404 is considered a successful round trip that happens to carry an error status, so without that check you will happily try to parse an error page as your data.

Doing things at the same time

Awaiting in sequence is often wasteful. This takes as long as both requests combined:

const user = await loadUser(1);
const posts = await loadPosts(1); // only starts after user finishes

If neither depends on the other, start both and wait together with Promise.all. Now it takes as long as the slower one:

const [user, posts] = await Promise.all([loadUser(1), loadPosts(1)]);

One caveat: Promise.all rejects as soon as any promise rejects. When you would rather see every result, success or failure, use Promise.allSettled instead.

Try it yourself

This calls a real public API. Try changing the id to something invalid like 9999 and watch the error path run.

Try it yourself
Loading playground...

What to remember

  • Asynchronous work is queued so the page stays responsive.
  • await needs an async function, and async functions always return a promise.
  • fetch does not throw on 404 or 500 — check response.ok yourself.
  • Use Promise.all for independent work that can run at the same time.

That is the JavaScript track. You now have the language fundamentals every other track here builds on.

Check yourself

4 questions · pass 3/4 to unlock Scope and Closures

up to 50
  1. 1.What does an async function always return?

  2. 2.What does await actually do?

  3. 3.How do you handle a rejected promise inside an async function?

  4. 4.You need three independent requests to finish. What is the best approach?

4 left to answer