Cơ bảncluster

Promise trong JavaScript là gì? Cách dùng cơ bản

8 phút đọc1 lượt xem
#promise javascript#promise là gì#promise trong javascript#then catch javascript#xử lý bất đồng bộ

Promise trong JavaScript là gì? Cách dùng cơ bản

Khi đọc code JavaScript, bạn có thể thấy các đoạn code với .then().catch() mà không hiểu chúng làm gì. Đây là cú pháp của Promise — một trong những khái niệm quan trọng nhất trong JavaScript hiện đại.

Nếu bạn chưa biết JavaScript là gì, hãy đọc bài JavaScript là gì? Giới thiệu cho người mới bắt đầu trước nhé.

Sau bài này, bạn sẽ hiểu được:

  • Promise là gì và tại sao cần dùng
  • 3 trạng thái của Promise
  • Cú pháp new Promise(), .then(), .catch()
  • Promise chaining và Promise.all()
  • Tại sao Promise tốt hơn Callback

Promise trong JavaScript là gì?

Promise là một đối tượng (object) đại diện cho kết quả của một thao tác bất đồng bộ. Tên "Promise" nghĩa là "lời hứa" — hứa rằng sẽ trả về kết quả trong tương lai, dù thành công hay thất bại.

Hãy tưởng tượng bạn đặt pizza online. Sau khi đặt, bạn nhận được một "lời hứa" giao hàng trong 30 phút. Trong lúc chờ, bạn vẫn có thể làm việc khác. Sau 30 phút, nếu pizza đến thì "thành công" (fulfilled), nếu hết hàng thì "thất bại" (rejected). Promise trong JavaScript hoạt động y hệt như vậy.

JavaScript là ngôn ngữ đơn luồng (single-threaded). Nếu chờ đồng bộ khi gọi API hay đọc file, trình duyệt sẽ bị "đóng băng". Promise được giới thiệu trong ES6 (2015) để xử lý bất đồng bộ một cách rõ ràng và dễ quản lý hơn.

3 Trạng thái của Promise

Mỗi Promise luôn ở một trong ba trạng thái:

Trạng thái Ý nghĩa
Pending Đang chờ xử lý — chưa có kết quả
Fulfilled Thao tác thành công — resolve() được gọi
Rejected Thao tác thất bại — reject() được gọi

Điểm quan trọng: một khi Promise đã chuyển sang Fulfilled hoặc Rejected, trạng thái đó không thể thay đổi nữa. Điều này giúp bạn xử lý kết quả một cách đáng tin cậy.

Cú pháp cơ bản của Promise

Để tạo một Promise, bạn dùng new Promise() với một hàm executor bên trong:

const myPromise = new Promise((resolve, reject) => {
  // Thao tác bất đồng bộ của bạn ở đây
  const success = true;

  if (success) {
    resolve("Thành công!");   // Chuyển sang Fulfilled
  } else {
    reject("Thất bại...");    // Chuyển sang Rejected
  }
});

myPromise
  .then(result => console.log(result))   // In: "Thành công!"
  .catch(error => console.log(error));   // Không chạy vì success = true

Giải thích từng phần:

  • new Promise((resolve, reject) => {...}) — Tạo Promise mới với hàm executor
  • resolve(value) — Gọi khi thành công; value sẽ được truyền vào .then()
  • reject(reason) — Gọi khi thất bại; reason sẽ được truyền vào .catch()
  • .then(callback) — Chạy khi Promise fulfilled
  • .catch(callback) — Chạy khi Promise rejected

Ví dụ thực tế — Giả lập gọi API với setTimeout

function layDuLieuNguoiDung(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (userId > 0) {
        resolve({ id: userId, ten: "Nguyen Van A", tuoi: 22 });
      } else {
        reject(new Error("userId không hợp lệ"));
      }
    }, 1000); // Giả lập delay 1 giây
  });
}

layDuLieuNguoiDung(1)
  .then(nguoiDung => {
    console.log("Tìm thấy:", nguoiDung.ten); // "Tìm thấy: Nguyen Van A"
  })
  .catch(loi => {
    console.error("Lỗi:", loi.message);
  });

Trong ví dụ này, setTimeout mô phỏng thao tác tốn thời gian (như gọi API thực tế). Sau 1 giây, nếu userId hợp lệ thì Promise fulfilled với dữ liệu người dùng, ngược lại bị rejected.

Promise Chaining — Nối tiếp nhiều Promise

Một trong những tính năng mạnh nhất của Promise là khả năng nối tiếp (chain) nhiều thao tác bất đồng bộ. Thay vì lồng nhau, bạn có thể viết theo dạng thẳng hàng, dễ đọc hơn nhiều:

fetch('https://api.example.com/data')
  .then(response => response.json())       // Bước 1: Parse JSON từ response
  .then(data => {
    console.log(data);                      // Bước 2: Dùng dữ liệu
    return data.userId;                     // Trả về userId cho bước tiếp theo
  })
  .then(userId => {
    return fetch(`https://api.example.com/users/${userId}`);
  })
  .then(response => response.json())
  .then(user => console.log(user.name))    // Bước cuối: In tên người dùng
  .catch(error => console.error(error));   // Bắt lỗi từ bất kỳ bước nào

Những điểm cần nhớ về Promise chaining:

  • Mỗi .then() nhận giá trị return của .then() trước đó
  • Nếu .then() trả về một Promise mới, chuỗi sẽ chờ Promise đó hoàn thành
  • Một .catch() ở cuối bắt được lỗi từ tất cả các bước trong chuỗi

Promise.all() — Chạy nhiều Promise song song

Khi cần nhiều tác vụ chạy đồng thời (ví dụ: load nhiều API một lúc để tiết kiệm thời gian), hãy dùng Promise.all():

const layUser1 = fetch('https://api.example.com/users/1').then(r => r.json());
const layUser2 = fetch('https://api.example.com/users/2').then(r => r.json());
const layUser3 = fetch('https://api.example.com/users/3').then(r => r.json());

Promise.all([layUser1, layUser2, layUser3])
  .then(([user1, user2, user3]) => {
    console.log(user1.name, user2.name, user3.name);
  })
  .catch(error => {
    console.error("Một trong các request thất bại:", error);
  });

Đặc điểm của Promise.all():

  • Fulfilled khi tất cả Promise trong mảng đều fulfilled
  • Rejected ngay lập tức nếu bất kỳ một Promise nào bị rejected
  • Kết quả trả về là mảng, theo đúng thứ tự input
  • Các Promise chạy song song → nhanh hơn chạy tuần tự

Mẹo: Nếu bạn muốn chờ tất cả kể cả khi có lỗi, hãy dùng Promise.allSettled() thay vì Promise.all().

Promise vs Callback Hell — Tại sao Promise tốt hơn?

Trước khi có Promise, lập trình viên xử lý bất đồng bộ bằng Callback trong JavaScript. Callback đơn giản thì ổn, nhưng khi lồng nhiều cấp sẽ tạo ra "Callback Hell" (hay còn gọi là "Pyramid of Doom"):

// Callback Hell — khó đọc, khó debug, khó bảo trì
getData(function(a) {
  getMoreData(a, function(b) {
    getEvenMoreData(b, function(c) {
      getYetMoreData(c, function(d) {
        console.log(d); // Thụt lề sâu tới mức khó theo dõi
      }, errorHandler);
    }, errorHandler);
  }, errorHandler);
}, errorHandler);

Cùng đoạn logic đó, viết bằng Promise:

// Promise Chain — dễ đọc, dễ bảo trì
getData()
  .then(a => getMoreData(a))
  .then(b => getEvenMoreData(b))
  .then(c => getYetMoreData(c))
  .then(d => console.log(d))
  .catch(errorHandler); // Một chỗ xử lý lỗi cho toàn bộ chuỗi

Lý do Promise tốt hơn Callback:

  1. Dễ đọc hơn — Cấu trúc tuyến tính, không bị "thụt lề vô tận"
  2. Xử lý lỗi tập trung — Một .catch() cho toàn bộ chuỗi
  3. Tránh Callback Hell — Không còn "Pyramid of Doom"
  4. Dễ thêm/bỏ bước xử lý — Chỉ cần thêm/xóa .then()
  5. Nền tảng cho async/await — Cú pháp hiện đại nhất trong JavaScript

Bước tiếp theo: async/await

Bạn đã nắm vững Promise — đây là nền tảng quan trọng nhất! Bước tiếp theo là học async/await, cú pháp được giới thiệu trong ES2017 giúp viết code bất đồng bộ trông giống code đồng bộ, dễ đọc hơn nhiều.

// Cùng chức năng, nhưng async/await dễ đọc hơn Promise chain
async function layDuLieu() {
  const response = await fetch('/api/data'); // Chờ fetch xong
  const data = await response.json();        // Chờ parse JSON xong
  return data.name;
}

Điều quan trọng cần nhớ: async/await được xây dựng trên nền Promise. Hiểu Promise rõ ràng là tiền đề để học async/await hiệu quả.

Hãy đọc bài tiếp theo: async/await trong JavaScript — Cách viết code bất đồng bộ hiện đại

Tóm tắt

  • Promise là đối tượng đại diện cho kết quả của thao tác bất đồng bộ
  • Ba trạng thái: pendingfulfilled (thành công) hoặc rejected (thất bại)
  • new Promise((resolve, reject) => {...}) để tạo Promise
  • .then() xử lý kết quả thành công, .catch() xử lý lỗi
  • Promise chaining cho phép nối nhiều bước xử lý bất đồng bộ
  • Promise.all() chạy nhiều Promise song song, nhanh hơn tuần tự
  • Promise giải quyết vấn đề Callback Hell, code dễ đọc và bảo trì hơn
  • async/await là bước tiếp theo, xây dựng trực tiếp trên Promise

Về tác giả

Ảnh đại diện tác giả Kenji — họa tiết hình học

Kenji

Kỹ sư phần mềm full-stack (Web), hơn 5 năm kinh nghiệm thực tế

  • Python
  • DB
  • Hạ tầng
  • Đào tạo & cố vấn
  • AI

Làm việc cùng đồng nghiệp người Việt, tôi thấy thiếu tài liệu kỹ thuật rõ ràng bằng tiếng Việt. codeahoc là nơi tôi chia sẻ theo hướng thực tế, dễ áp dụng.

Nguyên tắc nội dung

  • Ưu tiên nguồn gốc và góc nhìn từ thực tế triển khai.
  • Nếu có sai sót, nội dung sẽ được cập nhật và sửa kịp thời.

Khóa học liên quan

The Complete JavaScript Course 2024: From Zero to Expert!

Khóa học JavaScript toàn diện nhất từ cơ bản đến nâng cao.

4.7499.000 ₫
Xem khóa học →

React - The Complete Guide (incl. React Router & Redux)

Làm chủ React.js với các dự án thực tế, hooks, Redux.

4.6499.000 ₫
Xem khóa học →

Node.js, Express, MongoDB & More: The Complete Bootcamp

Backend với Node.js: REST API, authentication, MongoDB.

4.7499.000 ₫
Xem khóa học →
Quảng cáo