ducdev
Frontend / Bài viết

JavaScript Async/Await — Viết Code Bất Đồng Bộ Như Đồng Bộ

Async/await biến callback hell thành code sạch, dễ đọc. Bài viết này đi sâu vào cơ chế hoạt động, error handling, và các pattern nâng cao của async JavaScript.

a
admin
27/02/2026 · 3 phút đọc · 0 lượt xem
Chia sẻ

JavaScript là ngôn ngữ single-threaded, nhưng web cần xử lý rất nhiều thứ xảy ra "đồng thời": API calls, file reads, timers. Async/await là giải pháp hiện đại và thanh lịch nhất cho bài toán này.

Từ Callback Đến Promise Đến Async/Await

Để hiểu tại sao async/await quan trọng, hãy xem sự tiến hóa:

// Thời callback (2010s) — Callback hell
getData(function(data) {
  processData(data, function(result) {
    saveResult(result, function(saved) {
      console.log('Xong!');
    });
  });
});

// Promise (ES6) — Tốt hơn nhưng vẫn chain dài
getData()
  .then(data => processData(data))
  .then(result => saveResult(result))
  .then(() => console.log('Xong!'));

// Async/Await (ES2017) — Đọc như code đồng bộ
async function doWork() {
  const data = await getData();
  const result = await processData(data);
  await saveResult(result);
  console.log('Xong!');
}

Cơ Chế Hoạt Động

async biến hàm thành một hàm trả về Promise. await tạm dừng execution của async function và chờ Promise resolve — nhưng không block event loop, các task khác vẫn chạy bình thường.

async function layDanhSachBai() {
  const response = await fetch('/api/posts');
  const data = await response.json();
  return data;
}

// Gọi async function
layDanhSachBai().then(data => console.log(data));
// Hoặc
const data = await layDanhSachBai(); // Chỉ dùng được trong async context

Error Handling Với Try/Catch

async function layBaiViet(id) {
  try {
    const response = await fetch(`/api/posts/${id}`);

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

    return await response.json();
  } catch (error) {
    console.error('Lỗi khi lấy bài viết:', error);
    throw error; // Re-throw để caller xử lý
  }
}

Chạy Song Song Với Promise.all

Đây là anti-pattern phổ biến nhất — await tuần tự khi có thể chạy song song:

// Xấu — chạy tuần tự, tổng = 1s + 1s = 2s
const user = await layUser(userId);       // 1s
const posts = await layPosts(userId);     // 1s

// Tốt — chạy song song, tổng = 1s
const [user, posts] = await Promise.all([
  layUser(userId),
  layPosts(userId)
]);

Promise.allSettled — Khi Muốn Tất Cả Kết Quả Dù Có Lỗi

const results = await Promise.allSettled([
  fetchFromAPI1(),
  fetchFromAPI2(),
  fetchFromAPI3()
]);

results.forEach(result => {
  if (result.status === 'fulfilled') {
    console.log('Thành công:', result.value);
  } else {
    console.log('Lỗi:', result.reason);
  }
});

Xử Lý Danh Sách Bất Đồng Bộ

const userIds = [1, 2, 3, 4, 5];

// Chạy song song — nhanh nhưng nhiều request cùng lúc
const users = await Promise.all(
  userIds.map(id => layUser(id))
);

// Chạy tuần tự — khi cần kiểm soát rate
const users = [];
for (const id of userIds) {
  users.push(await layUser(id));
}
Async/await không xóa bỏ callbacks hay promises — nó chỉ là syntactic sugar giúp code đọc dễ hơn. Hiểu cơ chế Promise vẫn rất quan trọng.

Kết Luận

Async/await là công cụ không thể thiếu trong JavaScript hiện đại. Nắm vững try/catch cho error handling, Promise.all cho parallelism, và tránh await không cần thiết trong vòng lặp. Bước tiếp theo: tìm hiểu về AbortController để cancel async operations khi cần.

#JavaScript #Async #Promise #Frontend
a
Tác giả
admin

Lập trình viên, yêu thích chia sẻ kiến thức về công nghệ và phát triển phần mềm.

Bình luận

Chưa có bình luận. Hãy là người đầu tiên!

Để lại bình luận

Bình luận sẽ được duyệt trước khi hiển thị.