Home About Skills Products Work
Projects Services Experience
Learn
Tutorials Courses Blogs Resources
Contact
Blog · JavaScript

Async/Await and Promises in JavaScript: A Practical Guide

Callback hell was the original way JavaScript handled anything asynchronous, and it was miserable. Promises fixed the structure; async/await made them read like normal code. Here's how to actually reason about both.

What a Promise Actually Is

A Promise represents a value that doesn't exist yet, but will — either successfully (resolved) or with an error (rejected). Every fetch() call returns one:

fetch('/api/posts')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error('Failed:', err));

async/await: The Same Thing, Readable

async function loadPosts() {
  try {
    const res = await fetch('/api/posts');
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.error('Failed:', err);
  }
}

Under the hood this compiles to the exact same Promise chain — await is just syntax that pauses the function until the Promise settles, without blocking the rest of the browser.

Running Things in Parallel

A very common beginner mistake — awaiting one request, then the next, when neither depends on the other:

// Slow: waits for posts before even starting the users request
const posts = await getPosts();
const users = await getUsers();

// Fast: both start immediately
const [posts, users] = await Promise.all([getPosts(), getUsers()]);

Error Handling That Actually Catches Things

A rejected Promise inside an async function without a try/catch becomes an unhandled rejection — silently breaking your app in ways that are hard to debug. Always wrap awaited calls that can fail (which is: any network call, ever) in try/catch.

Promise.allSettled: When Some Failures Are OK

const results = await Promise.allSettled([getUserA(), getUserB(), getUserC()]);
// results: array of { status: 'fulfilled', value } or { status: 'rejected', reason }

Unlike Promise.all, this doesn't reject the whole batch if one request fails — useful when you want partial results rather than an all-or-nothing outcome.

Once async/await clicks, most of what looked like "advanced JavaScript" in framework tutorials (data fetching in React useEffect, Next.js Server Components) turns out to just be this same pattern applied in a specific place.

JavaScript Fundamentals Every Beginner Should Master

JavaScript Fundamentals Every Beginner Should Master

Variables, functions, arrays, objects, and the DOM — the plain JavaScript every framework is built on top of.

Modern JavaScript (ES6+) Features You Should Be Using

Modern JavaScript (ES6+) Features You Should Be Using

Template literals, destructuring, optional chaining, and modules — the JavaScript you actually see in every modern codebase.

React Hooks Explained: useState, useEffect, and Beyond

React Hooks Explained: useState, useEffect, and Beyond

A practical walkthrough of useState, useEffect, useRef, and useContext — the hooks that cover the majority of real React component logic.

Esc