Quay lại
Công Nghệ

JavaScript Asynchronous: Hiểu về Promise và Async/Await từ A-Z (Part 1)

6 phút đọc23 thg 8, 2026
N

Frontend Dzui Lắm

Tác giả

JavaScript Asynchronous: Hiểu về Promise và Async/Await từ A-Z (Part 1)

JavaScript là single-threaded, vậy tại sao nó vẫn có thể xử lý hàng trăm request, timer, event và các tác vụ bất đồng bộ mà không làm treo giao diện?

Để trả lời câu hỏi này, chúng ta cần hiểu cách JavaScript xử lý Asynchronous Programming, đặc biệt là Promiseasync/await.

Trong bài viết này, chúng ta sẽ đi từ nền tảng của JavaScript Runtime đến cách sử dụng Promise và Async/Await trong các ứng dụng Frontend thực tế.


1. JavaScript là Single-threaded

Một trong những đặc điểm quan trọng của JavaScript là:

JavaScript sử dụng một Call Stack duy nhất để thực thi JavaScript code.

Ví dụ:

console.log("A");
console.log("B");
console.log("C");

Code được thực thi theo thứ tự:

A
B
C

Tại một thời điểm, JavaScript chỉ thực thi một operation trên Call Stack.

Vậy nếu JavaScript chỉ có một thread thì chuyện gì xảy ra khi chúng ta gọi API?

const response = fetch("/api/users");

Request có thể mất vài trăm milliseconds hoặc thậm chí vài giây.

Nếu JavaScript phải đứng yên chờ request hoàn thành thì UI sẽ bị block.

Nhưng thực tế:

Đó là nhờ cơ chế asynchronous của JavaScript Runtime.


2. Synchronous vs Asynchronous

Synchronous

Code được thực hiện tuần tự:

console.log("A");

console.log("B");

console.log("C");

Flow:

A
 ↓
B
 ↓
C

Operation trước hoàn thành thì operation sau mới bắt đầu.


Asynchronous

Với asynchronous code:

console.log("A");

setTimeout(() => {
  console.log("B");
}, 1000);

console.log("C");

Kết quả:

A
C
B

JavaScript không đứng yên chờ setTimeout().

Nó đăng ký task và tiếp tục thực hiện code phía sau.


3. Event Loop

Để hiểu Promise và async/await, chúng ta cần hiểu một chút về Event Loop.

Có thể hình dung đơn giản:

                 JavaScript
                     │
                     ▼
                Call Stack
                     │
                     ▼
              Browser Runtime
                     │
                     ▼
              Task Queues
                     │
                     ▼
                 Event Loop
                     │
                     └──────► Call Stack

Ví dụ:

console.log("Start");

setTimeout(() => {
  console.log("Timeout");
}, 0);

console.log("End");

Kết quả:

Start
End
Timeout

setTimeout(..., 0) không có nghĩa callback chạy ngay lập tức.

Nó chỉ có nghĩa:

Đưa callback vào hàng đợi sau khi timer hoàn thành và chờ Event Loop đưa nó vào Call Stack khi phù hợp.


4. Callback Hell

Trước khi Promise trở nên phổ biến, asynchronous code thường được xử lý bằng callback.

Ví dụ:

getUser(userId, user => {

  getOrders(user.id, orders => {

    getProducts(orders, products => {

      getRecommendations(products, recommendations => {

        console.log(recommendations);

      });

    });

  });

});

Khi số lượng asynchronous operation tăng lên, code trở nên:

Đây thường được gọi là Callback Hell.

Promise được giới thiệu để giải quyết một phần lớn vấn đề này.


5. Promise là gì?

Promise là một object đại diện cho kết quả của một asynchronous operation.

Một Promise có 3 trạng thái:

             Pending
                │
        ┌───────┴───────┐
        ▼               ▼
    Fulfilled        Rejected

Pending

Operation vẫn đang được xử lý.

Fulfilled

Operation hoàn thành thành công.

Rejected

Operation thất bại.

Ví dụ:

const promise = fetch("/api/users");

fetch() trả về một Promise.

Chúng ta có thể xử lý kết quả:

fetch("/api/users")
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });

6. Promise chaining

Promise cho phép chúng ta chain nhiều asynchronous operation:

getUser()
  .then(user => getOrders(user.id))
  .then(orders => getOrderDetail(orders[0].id))
  .then(detail => {
    console.log(detail);
  })
  .catch(error => {
    console.error(error);
  });

Flow:

getUser()
   ↓
user
   ↓
getOrders()
   ↓
orders
   ↓
getOrderDetail()
   ↓
detail

Code đã dễ đọc hơn callback hell.

Tuy nhiên, với business logic phức tạp, Promise chaining vẫn có thể trở nên dài.

Đó là lý do async/await trở nên phổ biến.


7. Async/Await

async/await giúp chúng ta viết Promise-based code theo syntax gần giống synchronous code.

Ví dụ với Promise:

function getUser() {
  return fetch("/api/user")
    .then(response => response.json());
}

Có thể viết:

async function getUser() {
  const response = await fetch("/api/user");

  return response.json();
}

Code business logic trở nên dễ đọc hơn:

async function loadOrder() {
  const user = await getUser();

  const orders = await getOrders(user.id);

  const detail = await getOrderDetail(orders[0].id);

  return detail;
}

8. async function luôn trả về Promise

Đây là điểm rất quan trọng.

async function hello() {
  return "Hello";
}

Mặc dù chúng ta return một string:

return "Hello";

nhưng kết quả của function vẫn là Promise.

const result = hello();

console.log(result);

Kết quả về bản chất là:

Promise

Có thể sử dụng:

hello().then(value => {
  console.log(value);
});

Hoặc:

const value = await hello();

9. await thực sự làm gì?

Ví dụ:

async function test() {

  console.log("A");

  await delay(1000);

  console.log("B");

}

await có nghĩa là:

Tạm dừng việc thực thi async function hiện tại cho đến khi Promise hoàn thành.

không có nghĩa là block toàn bộ JavaScript thread.

Ví dụ:

async function test() {
  console.log("A");

  await delay(1000);

  console.log("B");
}

test();

console.log("C");

Kết quả:

A
C
B

10. Promise vs Async/Await

Hai cách viết:

Promise

getUser()
  .then(user => {
    return getOrders(user.id);
  })
  .then(orders => {
    console.log(orders);
  })
  .catch(error => {
    console.error(error);
  });

Async/Await

try {
  const user = await getUser();

  const orders = await getOrders(user.id);

  console.log(orders);

} catch (error) {
  console.error(error);
}

Về bản chất:

Async/Await
     ↓
Syntax trên Promise

Async/await không thay thế Promise.

Nó là một cách viết dễ đọc hơn khi làm việc với Promise.


11. Khi nào nên sử dụng Promise?

Promise chaining có thể phù hợp khi:

Ví dụ:

getUser()
  .then(validateUser)
  .then(createSession)
  .then(loadDashboard);

12. Khi nào nên sử dụng Async/Await?

Async/await thường rất dễ đọc khi xử lý business logic:

async function checkout() {

  try {

    const cart = await getCart();

    const payment = await createPayment(cart);

    const order = await createOrder(payment);

    return order;

  } catch (error) {

    console.error(error);

  }
}

Đặc biệt phù hợp với:


13. Một hiểu lầm phổ biến về await

Nhiều developer nghĩ:

"Dùng await nghĩa là mọi request đều chạy tuần tự."

Điều này không hoàn toàn đúng.

Vấn đề nằm ở cách chúng ta tạo Promise.

Ví dụ:

const users = await getUsers();

const products = await getProducts();

const orders = await getOrders();

Ở đây các request được xử lý tuần tự.

Nhưng nếu:

const usersPromise = getUsers();
const productsPromise = getProducts();
const ordersPromise = getOrders();

const users = await usersPromise;
const products = await productsPromise;
const orders = await ordersPromise;

thì cả 3 Promise đã được tạo trước.

Đây chính là nền tảng để chúng ta xử lý nhiều request đồng thời.

Và đây sẽ là nội dung chính của Part 2.


14. Tổng kết Part 1

Có thể ghi nhớ:

JavaScript
    ↓
Single-threaded
    ↓
Event Loop
    ↓
Asynchronous
    ↓
Promise
    ↓
Async/Await

Các ý quan trọng nhất:

Part 2, chúng ta sẽ đi vào phần quan trọng hơn trong thực tế Frontend:

Làm thế nào để xử lý 10, 100 hoặc hàng nghìn request hiệu quả mà không khiến ứng dụng và server bị quá tải?

Thích bài viết này?

Nội dung trên Vết Mực luôn được chia sẻ miễn phí. Nếu bài viết mang lại giá trị cho bạn, hãy cân nhắc ủng hộ để chúng mình có thể duy trì máy chủ, phát triển thêm tính năng mới và tiếp tục xây dựng một không gian dành cho những người yêu viết lách. ✨

Các cách ủng hộ:

  • Viết và đăng bài trên Vết Mực
  • Chia sẻ bài viết với bạn bè
  • Góp ý để chúng mình cải thiện sản phẩm qua email: nsikhoa@gmail.com

Dù bạn chọn ủng hộ hay chỉ đơn giản là tiếp tục đọc và chia sẻ bài viết, đó đều là nguồn động lực rất lớn với chúng mình. ❤️

Bình luận

Đăng nhập để để lại bình luận.