惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
F
Fortinet All Blogs
爱范儿
爱范儿
博客园 - Franky
MyScale Blog
MyScale Blog
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
B
Blog
P
Proofpoint News Feed
IT之家
IT之家
宝玉的分享
宝玉的分享
D
DataBreaches.Net
S
SegmentFault 最新的问题
Microsoft Azure Blog
Microsoft Azure Blog
GbyAI
GbyAI
M
MIT News - Artificial intelligence
L
LangChain Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
月光博客
月光博客
雷峰网
雷峰网
Stack Overflow Blog
Stack Overflow Blog
量子位
V
V2EX
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Async/Await in JavaScript: Writing Cleaner Asynchronous Code
Pratham · 2026-05-10 · via DEV Community

Asynchronous code that reads like synchronous code — because sometimes the best upgrade is better syntax.


We've come a long way. Callbacks taught us how JavaScript handles async operations. Promises gave us flat chains and centralized error handling. But let me show you the next evolution:

// Promise chain
getUser(1)
  .then((user) => getOrders(user.id))
  .then((orders) => getShipping(orders[0].id))
  .then((shipping) => console.log(shipping.address))
  .catch((err) => console.log(err));

// Async/await — same logic
async function showShippingAddress() {
  try {
    const user = await getUser(1);
    const orders = await getOrders(user.id);
    const shipping = await getShipping(orders[0].id);
    console.log(shipping.address);
  } catch (err) {
    console.log(err);
  }
}

Enter fullscreen mode Exit fullscreen mode

Look at the async/await version. No .then(). No chaining. No callbacks. It reads exactly like synchronous code — step 1, step 2, step 3 — but it's fully asynchronous under the hood.

When I first saw this in the ChaiCode Web Dev Cohort 2026, I thought it was too good to be true. But it's real, and it's now the standard way to write async JavaScript. Let me break it down.


Why Was Async/Await Introduced?

Promises were a massive improvement over callbacks. But they still have a learning curve. .then() chains, returning values between steps, understanding how .catch() propagates — it's all manageable, but it's not intuitive.

The JavaScript community asked: "What if we could write async code that looks like regular, top-to-bottom, synchronous code?"

That's exactly what async/await is. It was introduced in ES2017 (ES8) as syntactic sugar on top of Promises. It doesn't replace Promises — it uses them. Every async function returns a Promise. Every await unwraps a Promise. It's Promises with a friendlier face.

The Evolution

Callbacks (ES5):
  getUser(1, (err, user) => {
    getOrders(user.id, (err, orders) => {
      getShipping(orders[0].id, (err, ship) => {
        console.log(ship.address);
      });
    });
  });

Promises (ES6):
  getUser(1)
    .then(user => getOrders(user.id))
    .then(orders => getShipping(orders[0].id))
    .then(ship => console.log(ship.address))
    .catch(err => console.log(err));

Async/Await (ES2017):
  const user = await getUser(1);
  const orders = await getOrders(user.id);
  const shipping = await getShipping(orders[0].id);
  console.log(shipping.address);

Same async operations. Each version is cleaner than the last.

Enter fullscreen mode Exit fullscreen mode


How Async Functions Work

An async function is a function declared with the async keyword. Two things happen when you add async:

  1. The function automatically returns a Promise
  2. You're allowed to use the await keyword inside it

Basic Syntax

async function fetchData() {
  return "Hello from async!";
}

// This is identical to:
function fetchData() {
  return Promise.resolve("Hello from async!");
}

Enter fullscreen mode Exit fullscreen mode

Whatever you return from an async function gets wrapped in a resolved Promise automatically.

async function getName() {
  return "Pratham";
}

getName().then((name) => console.log(name)); // "Pratham"

Enter fullscreen mode Exit fullscreen mode

You can also use arrow function syntax:

const getName = async () => {
  return "Pratham";
};

Enter fullscreen mode Exit fullscreen mode

Proof That Async Functions Return Promises

async function example() {
  return 42;
}

const result = example();
console.log(result); // Promise { <fulfilled>: 42 }
console.log(result instanceof Promise); // true

Enter fullscreen mode Exit fullscreen mode

It's a Promise. Always. Even if you return a plain value, it gets wrapped.


The await Keyword

await is the magic word. It pauses the execution of the async function until the Promise it's waiting on settles (fulfills or rejects). Then it unwraps the result and gives you the actual value.

Without await

async function getUser() {
  const response = fetch("https://jsonplaceholder.typicode.com/users/1");
  console.log(response); // Promise { <pending> } — not the data!
}

Enter fullscreen mode Exit fullscreen mode

With await

async function getUser() {
  const response = await fetch(
    "https://jsonplaceholder.typicode.com/users/1",
  );
  const user = await response.json();
  console.log(user.name); // "Leanne Graham" — the actual data!
}

getUser();

Enter fullscreen mode Exit fullscreen mode

await says: "Pause here. Wait for this Promise to resolve. Then give me the value."

Key Rules for await

  1. await can only be used inside async functions (or at the top level of a module)
  2. await pauses the async function, not the entire program — other code keeps running
  3. await works with any Promise — built-in ones like fetch() or your own

await Doesn't Block Everything

This is crucial to understand. When an async function hits await, it pauses that function and gives control back to the rest of the program. Other code continues running.

async function slowTask() {
  console.log("A: Starting slow task...");
  await new Promise((resolve) => setTimeout(resolve, 2000));
  console.log("B: Slow task done!");
}

console.log("1: Before");
slowTask();
console.log("2: After");

Enter fullscreen mode Exit fullscreen mode

Output:

1: Before
A: Starting slow task...
2: After
B: Slow task done!      ← 2 seconds later

Enter fullscreen mode Exit fullscreen mode

"2: After" prints before "B: Slow task done!" because await only pauses the async function, not the calling code.


Async Function Execution Flow

console.log("Start");

async function doWork() {
  console.log("Step 1");
  const data = await fetchSomething();    pauses HERE
  console.log("Step 2:", data);            runs after await resolves
}

doWork();
console.log("End");

Execution:
─────────────────────────────────────────────────
  "Start"          synchronous
  "Step 1"         synchronous (inside async, before await)
  "End"            synchronous (doWork paused at await, control returned)
  ...waiting for fetchSomething()...
  "Step 2: data"   runs when the Promise resolves
─────────────────────────────────────────────────

Key insight: Everything BEFORE the first await runs synchronously.
At the first await, the function pauses and control returns to the caller.

Enter fullscreen mode Exit fullscreen mode


Error Handling with try/catch

With Promises, you use .catch(). With async/await, you use try/catch — the same error handling syntax you use for synchronous code.

Basic Error Handling

async function fetchUser(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const user = await response.json();
    console.log(`User: ${user.name}`);
  } catch (error) {
    console.log(`Failed to fetch user: ${error.message}`);
  }
}

fetchUser(1);

Enter fullscreen mode Exit fullscreen mode

If anything inside the try block throws an error or if any awaited Promise rejects, execution immediately jumps to the catch block.

With finally

async function loadData() {
  console.log("⏳ Loading...");

  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
    const post = await response.json();
    console.log(`✅ Loaded: ${post.title}`);
  } catch (error) {
    console.log(`❌ Error: ${error.message}`);
  } finally {
    console.log("🏁 Loading complete — cleanup done.");
  }
}

loadData();

Enter fullscreen mode Exit fullscreen mode

finally runs no matter what — perfect for hiding loading spinners, closing connections, or any cleanup.

Handling Multiple Awaits

Each await in a try block is covered by the same catch. If step 2 fails, you don't need a separate error handler:

async function processOrder() {
  try {
    const user = await getUser(1);
    const orders = await getOrders(user.id); // If THIS fails...
    const shipping = await getShipping(orders[0].id);
    console.log(`Shipping to: ${shipping.address}`);
  } catch (error) {
    console.log(`Something failed: ${error.message}`); // ...it's caught HERE
  }
}

Enter fullscreen mode Exit fullscreen mode

Compare this to the Promise version where .catch() at the end catches errors from any step — same behavior, but try/catch reads more naturally.


Promise vs Async/Await — Side by Side

Sequential Operations

// Promise chain
function getProfile() {
  return getUser(1)
    .then((user) => {
      return getAvatar(user.avatarId);
    })
    .then((avatar) => {
      console.log(`Avatar URL: ${avatar.url}`);
    })
    .catch((err) => {
      console.log(err);
    });
}

// Async/await
async function getProfile() {
  try {
    const user = await getUser(1);
    const avatar = await getAvatar(user.avatarId);
    console.log(`Avatar URL: ${avatar.url}`);
  } catch (err) {
    console.log(err);
  }
}

Enter fullscreen mode Exit fullscreen mode

Error Handling

// Promise
fetchData()
  .then((data) => processData(data))
  .then((result) => saveResult(result))
  .catch((err) => console.log("Error:", err));

// Async/await
async function handleData() {
  try {
    const data = await fetchData();
    const result = await processData(data);
    await saveResult(result);
  } catch (err) {
    console.log("Error:", err);
  }
}

Enter fullscreen mode Exit fullscreen mode

Comparison Table

Feature Promises (.then()) Async/Await
Syntax .then() / .catch() chains await + try/catch
Readability Good — but chains can get long Excellent — reads like sync code
Error handling .catch() at the end try/catch — familiar syntax
Debugging Harder — stack traces less clear Easier — breakpoints work normally
Line-by-line Need to follow the chain Each await is its own line
Under the hood Promises Also Promises (syntactic sugar)
When to use Simple chains, .all(), .race() Sequential async, most use cases

Promise vs Async/Await Flow

PROMISE CHAIN:
  getUser(1) ──→ .then() ──→ .then() ──→ .then() ──→ .catch()
                   │            │            │           │
              returns a    returns a    returns a    catches any
              Promise      Promise      Promise      rejection

ASYNC/AWAIT:
  async function() {
    const user = await getUser(1);      ← pause, get value
    const orders = await getOrders();   ← pause, get value
    const ship = await getShipping();   ← pause, get value
    console.log(ship.address);          ← use the value
  }

Same thing. Different syntax. Async/await just reads top-to-bottom.

Enter fullscreen mode Exit fullscreen mode


Practical Patterns

Pattern 1: Fetching Data from a Real API

async function displayUser() {
  try {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/users/1",
    );
    const user = await response.json();

    console.log(`Name: ${user.name}`);
    console.log(`Email: ${user.email}`);
    console.log(`City: ${user.address.city}`);
  } catch (error) {
    console.log("Failed to fetch user:", error.message);
  }
}

displayUser();

Enter fullscreen mode Exit fullscreen mode

Pattern 2: Sequential Operations

async function morningRoutine() {
  const coffee = await makeCoffee(); // Wait for coffee first
  const breakfast = await cookBreakfast(); // Then cook breakfast
  const news = await fetchNews(); // Then fetch news

  console.log(`Enjoying ${coffee} with ${breakfast} while reading ${news}`);
}

Enter fullscreen mode Exit fullscreen mode

Each step waits for the previous one. Order matters here.

Pattern 3: Parallel Operations with Promise.all()

Sometimes steps are independent — they don't depend on each other. Running them sequentially wastes time:

// ❌ Sequential — slow (3 seconds total)
async function loadDashboard() {
  const user = await fetchUser(); // 1 second
  const posts = await fetchPosts(); // 1 second
  const notifications = await fetchNotifications(); // 1 second
  // Total: 3 seconds 😴
}

// ✅ Parallel — fast (1 second total)
async function loadDashboard() {
  const [user, posts, notifications] = await Promise.all([
    fetchUser(), // 1 second ─┐
    fetchPosts(), // 1 second  ├─ all run simultaneously
    fetchNotifications(), // 1 second ─┘
  ]);
  // Total: ~1 second 🚀
}

Enter fullscreen mode Exit fullscreen mode

Promise.all() runs all three fetches at the same time and waits for all of them to finish. If any one fails, the entire Promise.all() rejects.

Pattern 4: Async in Arrow Functions

const getUser = async (id) => {
  const response = await fetch(`https://api.example.com/users/${id}`);
  return response.json();
};

const user = await getUser(1);

Enter fullscreen mode Exit fullscreen mode


Let's Practice: Hands-On Assignment

Part 1: Basic Async/Await

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

async function countdown() {
  console.log("3...");
  await delay(1000);
  console.log("2...");
  await delay(1000);
  console.log("1...");
  await delay(1000);
  console.log("🚀 Go!");
}

countdown();

Enter fullscreen mode Exit fullscreen mode

Part 2: Fetch Real Data

async function getPost() {
  try {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/posts/1",
    );
    const post = await response.json();

    console.log(`Title: ${post.title}`);
    console.log(`Body: ${post.body}`);
  } catch (error) {
    console.log("Error:", error.message);
  }
}

getPost();

Enter fullscreen mode Exit fullscreen mode

Part 3: Sequential vs Parallel

const fakeAPI = (name, ms) =>
  new Promise((resolve) => {
    setTimeout(() => resolve(`${name} loaded`), ms);
  });

// Sequential — runs one after another
async function sequential() {
  console.time("Sequential");
  const a = await fakeAPI("Users", 1000);
  const b = await fakeAPI("Posts", 1000);
  const c = await fakeAPI("Comments", 1000);
  console.log(a, b, c);
  console.timeEnd("Sequential"); // ~3 seconds
}

// Parallel — runs all at once
async function parallel() {
  console.time("Parallel");
  const [a, b, c] = await Promise.all([
    fakeAPI("Users", 1000),
    fakeAPI("Posts", 1000),
    fakeAPI("Comments", 1000),
  ]);
  console.log(a, b, c);
  console.timeEnd("Parallel"); // ~1 second
}

sequential();
// parallel();  // Try this one too!

Enter fullscreen mode Exit fullscreen mode

Part 4: Error Handling

async function riskyFetch() {
  try {
    const response = await fetch("https://invalid-url.example.com");
    const data = await response.json();
    console.log("Data:", data);
  } catch (error) {
    console.log("Caught an error:", error.message);
  } finally {
    console.log("Fetch attempt complete.");
  }
}

riskyFetch();

Enter fullscreen mode Exit fullscreen mode


Key Takeaways

  1. Async/await is syntactic sugar over Promises. It doesn't replace them — it makes them easier to write and read.
  2. async makes a function return a Promise. await pauses the function until a Promise resolves, then gives you the value.
  3. await only pauses the async function, not the entire program. Other code continues running while the function waits.
  4. Error handling uses try/catch — the same familiar syntax from synchronous JavaScript. Add finally for cleanup.
  5. Use Promise.all() for independent operations that can run in parallel. Use sequential await when each step depends on the previous one.

Wrapping Up

Async/await is the final piece of the async JavaScript puzzle. Callbacks showed us the concept. Promises gave us structure. Async/await gave us readability. The fact that you can write asynchronous code that looks and reads like regular, top-to-bottom JavaScript — with proper error handling and no nesting — is genuinely one of the best things about modern JavaScript.

I'm learning all of this through the ChaiCode Web Dev Cohort 2026 under Hitesh Chaudhary and Piyush Garg. The journey from callbacks → Promises → async/await has been one of the most satisfying progressions in the cohort. Each one builds on the last, and by the time you reach async/await, it all just clicks.

Connect with me on LinkedIn or visit PrathamDEV.in. More articles coming as the journey continues.

Happy coding! 🚀


Written by Pratham Bhardwaj | Web Dev Cohort 2026, ChaiCode