~/techpuraistart learning
~/tutorials/understanding-async-await
beginner·standalone·2 min read

Understanding async/await in JavaScript

Updated Jul 20, 2026JavaScript

Asynchronous code is the part of JavaScript that trips up the most beginners — not because the concept is hard, but because the syntax used to get there changed twice. Here's why async/await won, and how to actually reason about it.

The callback problem

Early JavaScript handled anything asynchronous — reading a file, calling an API — with callback functions. Nesting a few of these together produces what's usually called "callback hell":

js
getUser(id, (user) => {
  getPosts(user.id, (posts) => {
    getComments(posts[0].id, (comments) => {
      console.log(comments);
    });
  });
});

Each step only knows how to continue by calling the next function, and error handling has to be repeated at every level.

Promises flattened the nesting

A Promise represents a value that will exist eventually. Chaining .then() calls flattens the pyramid above:

js
getUser(id)
  .then((user) => getPosts(user.id))
  .then((posts) => getComments(posts[0].id))
  .then((comments) => console.log(comments))
  .catch((err) => console.error(err));

Better, but you're still writing in a style built around callbacks — just chained instead of nested.

async/await reads like synchronous code

await pauses execution inside an async function until a promise resolves, letting you write the same logic as if each step happened in order:

js
async function loadComments(id) {
  try {
    const user = await getUser(id);
    const posts = await getPosts(user.id);
    const comments = await getComments(posts[0].id);
    console.log(comments);
  } catch (err) {
    console.error(err);
  }
}

Nothing about the underlying behavior changed — it's still promises under the hood, and await only works inside an async function. What changed is that a single try/catch handles errors from every step, and the logic reads top to bottom.

Common mistake

await only pauses the function it's inside — it doesn't make the rest of your program synchronous. Calling an async function without awaiting it will run the rest of your code before that function finishes.

When you still want Promise methods directly

If you need to run several independent async operations at once rather than one after another, reach for Promise.all instead of awaiting each in sequence:

js
const [user, settings] = await Promise.all([
  getUser(id),
  getSettings(id),
]);

Awaiting these one at a time would double the wait for no reason — they don't depend on each other.