Bạn đang vật lộn với Callback Hell? Bài viết này sẽ giải thích chi tiết Promise là gì, 3 trạng thái, cách sử dụng .then, .catch, .finally và cú pháp async/await để xử lý bất đồng bộ trong JavaScript một cách hiệu quả.
Trong thế giới lập trình JavaScript hiện đại, việc xử lý các tác vụ bất đồng bộ (asynchronous) là điều không thể tránh khỏi, từ việc gọi API, đọc file cho đến các thao tác cần thời gian phản hồi. Trước đây, chúng ta thường dùng Callback, nhưng nó nhanh chóng dẫn đến một “cơn ác mộng” mang tên Callback Hell. Và Promise ra đời như một vị cứu tinh, mang đến một cách viết code sạch sẽ, dễ đọc và dễ quản lý hơn.
Vậy Promise là gì? Làm thế nào để sử dụng nó một cách hiệu quả? Hãy cùng khám phá tất tần tật trong bài viết này nhé!
1. Promise là gì? Một ví dụ dễ hiểu
Hãy tưởng tượng bạn đặt hàng online (ví dụ: gọi một API để lấy dữ liệu).
Promise (lời hứa) chính là một đối tượng đại diện cho một “lời hứa” từ người bán hàng (API server). “Lời hứa” này có nội dung: “Tôi đã nhận được yêu cầu của bạn. Tôi hứa sẽ xử lý và trả về kết quả trong tương lai. Kết quả đó có thể là hàng đã được giao thành công hoặc đơn hàng bị hủy vì hết hàng.”

Trong JavaScript, Promise là một đối tượng (object) đại diện cho sự hoàn thành hoặc thất bại (trong tương lai) của một tác vụ bất đồng bộ.
Một Promise sẽ có 3 trạng thái:
pending: Trạng thái ban đầu, “lời hứa” đang được xử lý (chờ giao hàng).fulfilled: Trạng thái hoàn thành, tác vụ đã thành công và trả về một giá trị (hàng được giao thành công).rejected: Trạng thái thất bại, tác vụ đã gặp lỗi và trả về một lý do (đơn hàng bị hủy).
Một khi Promise đã chuyển từ pending sang fulfilled hoặc rejected, trạng thái của nó sẽ không bao giờ thay đổi nữa.
2. Tại sao phải dùng Promise? Cứu tinh của “Callback Hell”
Để hiểu rõ sức mạnh của Promise, hãy xem xét ví dụ về “Callback Hell”. Giả sử bạn cần thực hiện 3 tác vụ bất đồng bộ nối tiếp nhau: lấy thông tin người dùng, sau đó lấy danh sách bài viết của người đó, rồi lấy tiếp bình luận của bài viết đầu tiên.

Nếu dùng Callback:
getUser(1, (user) => {
console.log('Lấy được user:', user);
getPosts(user.id, (posts) => {
console.log('Lấy được posts:', posts);
getComments(posts[0].id, (comments) => {
console.log('Lấy được comments:', comments);
// Và nếu có thêm tác vụ nữa... code sẽ càng thụt vào trong
});
});
});Đoạn code trên có cấu trúc “kim tự tháp ngược”, rất khó đọc, khó bảo trì và khó xử lý lỗi. Đây chính là Callback Hell.
Khi Promise xuất hiện:
getUser(1)
.then(user => {
console.log('Lấy được user:', user);
return getPosts(user.id); // Trả về một Promise khác
})
.then(posts => {
console.log('Lấy được posts:', posts);
return getComments(posts[0].id); // Lại trả về một Promise khác
})
.then(comments => {
console.log('Lấy được comments:', comments);
})
.catch(error => {
console.error('Đã có lỗi xảy ra:', error); // Bắt lỗi cho cả chuỗi
});Rõ ràng, code sử dụng Promise trông phẳng hơn, dễ đọc theo trình tự từ trên xuống dưới và việc xử lý lỗi cũng tập trung ở một nơi duy nhất với .catch().
Xem thêm:
3. Cách tạo và sử dụng một Promise
Cú pháp khởi tạo
Chúng ta tạo một Promise bằng constructor new Promise. Nó nhận vào một hàm (executor) với hai tham số là resolve và reject.
resolve(value): Hàm được gọi khi tác vụ thành công, trả về giá trịvalue.reject(error): Hàm được gọi khi tác vụ thất bại, trả về lý doerror.

const myPromise = new Promise((resolve, reject) => {
const isSuccess = true; // Giả sử tác vụ thành công
setTimeout(() => {
if (isSuccess) {
resolve("Dữ liệu đã được xử lý thành công!"); // Chuyển sang trạng thái fulfilled
} else {
reject("Đã có lỗi xảy ra trong quá trình xử lý."); // Chuyển sang trạng thái rejected
}
}, 2000); // Giả lập một tác vụ tốn 2 giây
});Sử dụng Promise
Để “nhận” kết quả từ Promise, chúng ta sử dụng các phương thức .then(), .catch(), và .finally().
.then(onFulfilled, onRejected)
Phương thức này nhận vào 2 hàm callback:
onFulfilled: Được thực thi khi Promise ở trạng tháifulfilled.onRejected: (Tùy chọn) Được thực thi khi Promise ở trạng tháirejected.
myPromise.then(
(successMessage) => {
// onFulfilled
console.log("Thành công:", successMessage);
},
(errorMessage) => {
// onRejected
console.error("Thất bại:", errorMessage);
}
);.catch(onRejected)
Đây là cú pháp “ngọt ngào” hơn, chuyên dùng để bắt lỗi (thay cho việc truyền hàm thứ hai vào .then()). Đây là cách được khuyến khích sử dụng.
myPromise
.then((successMessage) => {
console.log("Thành công:", successMessage);
})
.catch((errorMessage) => {
console.error("Thất bại:", errorMessage);
});.finally(onFinally)
Hàm callback bên trong .finally() sẽ luôn được thực thi dù Promise thành công hay thất bại. Nó rất hữu ích cho các tác vụ dọn dẹp như tắt loading spinner.
showLoadingSpinner(); // Hiện loading
myPromise
.then((data) => console.log(data))
.catch((error) => console.error(error))
.finally(() => {
hideLoadingSpinner(); // Luôn ẩn loading dù thành công hay thất bại
});4. Các phương thức tĩnh hữu ích của Promise
Ngoài các phương thức trên, đối tượng Promise còn cung cấp các phương thức tĩnh mạnh mẽ.
Promise.all(iterable): Nhận vào một mảng các Promise và trả về một Promise mới. Promise mới này sẽfulfilledkhi tất cả Promise trong mảng đềufulfilled, và sẽrejectedngay khi có bất kỳ Promise nào trong mảngrejected. Rất hữu ích khi bạn cần thực hiện nhiều tác vụ bất đồng bộ song song.JavaScriptPromise.all([ fetch('api/users/1'), fetch('api/products/1') ]) .then(([userResponse, productResponse]) => { // Xử lý khi cả hai API đều gọi thành công }) .catch(error => console.error('Một trong các API đã thất bại', error));Promise.race(iterable): Nhận vào một mảng các Promise và trả về một Promise mới. Promise mới này sẽ được giải quyết (fulfilled hoặc rejected) ngay khi Promise đầu tiên trong mảng được giải quyết. Giống như một cuộc đua, ai về đích trước sẽ quyết định kết quả.JavaScriptconst p1 = new Promise(resolve => setTimeout(resolve, 500, 'one')); const p2 = new Promise(resolve => setTimeout(resolve, 100, 'two')); Promise.race([p1, p2]).then(value => { console.log(value); // "two" - vì p2 hoàn thành trước });
5. Từ Promise đến Async/Await: Một cú pháp thanh lịch hơn
ES7 (ECMAScript 2017) đã giới thiệu async/await, một cú pháp giúp làm việc với Promise trở nên dễ dàng và trực quan hơn nữa. Về bản chất, nó chỉ là “syntactic sugar” (cú pháp thay thế) cho Promise, giúp code trông giống như code đồng bộ thông thường.
async: Đặt trước một hàm để cho biết hàm đó luôn trả về một Promise.await: Chỉ có thể được sử dụng bên trong một hàmasync. Nó sẽ tạm dừng việc thực thi hàm, chờ cho đến khi Promise được giải quyết, và sau đó trả về kết quả.
Hãy viết lại ví dụ “Callback Hell” ở trên bằng async/await:
async function fetchUserData() {
try {
const user = await getUser(1);
console.log('Lấy được user:', user);
const posts = await getPosts(user.id);
console.log('Lấy được posts:', posts);
const comments = await getComments(posts[0].id);
console.log('Lấy được comments:', comments);
} catch (error) {
console.error('Đã có lỗi xảy ra:', error); // Xử lý lỗi bằng try...catch
}
}
fetchUserData();Bạn có thể thấy, code với async/await cực kỳ sạch sẽ, dễ đọc và dễ hiểu, giống hệt như cách chúng ta viết code đồng bộ tuần tự. Việc xử lý lỗi được thực hiện bằng khối try...catch quen thuộc.
Kết luận
Promise không phải là một khái niệm quá phức tạp. Nó là một “lời hứa” về một giá trị sẽ có trong tương lai, giúp chúng ta thoát khỏi “Callback Hell” và quản lý code bất đồng bộ một cách mạch lạc. Bằng việc nắm vững cách tạo Promise, sử dụng .then(), .catch(), .finally(), và đặc biệt là áp dụng cú pháp hiện đại async/await, bạn đã có trong tay một công cụ vô cùng mạnh mẽ để xây dựng các ứng dụng JavaScript hiệu quả và chuyên nghiệp.
Chúc bạn thành công trên con đường chinh phục JavaScript!

Công nghệ tương lai Lập trình/ Code
Portainer Là Gì? Toàn Tập Về Công Cụ Quản Trị Container Hàng Đầu (Hướng Dẫn Chi Tiết)
Sự bùng nổ của công nghệ Container hóa (Containerization) với đầu tàu là Docker
Xem thêmTh3
Công nghệ tương lai Công cụ và hướng dẫn Lập trình/ Code
Helper Là Gì? Bí Quyết Viết Code “Sạch” Và Tối Ưu Trong Lập Trình
Trong thế giới lập trình và phát triển phần mềm, việc phải lặp đi
Xem thêmTh3
Digital Maketing Đồ Họa và Video Xu hướng
Des là gì? Giải mã ý nghĩa của Des trong Thiết kế, SEO, IT & Logistics
Bạn đang lướt mạng xã hội và thấy ai đó bình luận: “Dân Des
Xem thêmTh3