Hiểu về Bất Đồng Bộ trong JavaScript: Callback, Promise và Async/Await
Giới thiệu
JavaScript là một ngôn ngữ lập trình đơn luồng (single-threaded), nhưng nó có khả năng xử lý các tác vụ bất đồng bộ một cách hiệu quả nhờ vào cơ chế Event Loop. Trong bài viết này, chúng ta sẽ tìm hiểu về bất đồng bộ trong JavaScript, các phương pháp xử lý như Callback, Promise, và Async/Await, cùng với các ví dụ minh họa.
Bất Đồng Bộ là gì?
Bất đồng bộ (Asynchronous) là cách xử lý các tác vụ mà không cần chờ tác vụ trước đó hoàn thành. Điều này rất hữu ích khi làm việc với các tác vụ tốn thời gian như gọi API, đọc/ghi file, hoặc truy vấn cơ sở dữ liệu.
Callback
Callback là gì?
Callback là một hàm được truyền vào một hàm khác như một tham số và được gọi lại sau khi tác vụ hoàn thành.
Ví dụ về Callback
function fetchData(callback) {
setTimeout(() => {
console.log("Dữ liệu đã được tải");
callback();
}, 2000);
}
function processData() {
console.log("Xử lý dữ liệu...");
}
fetchData(processData);
// Output:
// Dữ liệu đã được tải
// Xử lý dữ liệu...
Hạn chế của Callback
- Callback Hell: Khi có nhiều callback lồng nhau, mã nguồn trở nên khó đọc và bảo trì.
- Khó xử lý lỗi: Việc bắt lỗi trong callback phức tạp hơn.
Promise
Promise là gì?
Promise là một đối tượng đại diện cho một tác vụ bất đồng bộ. Nó có ba trạng thái:
- Pending: Đang chờ xử lý.
- Fulfilled: Hoàn thành thành công.
- Rejected: Bị từ chối (lỗi).
Ví dụ về Promise
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // Thay đổi giá trị này để kiểm tra
if (success) {
resolve("Dữ liệu đã được tải");
} else {
reject("Lỗi khi tải dữ liệu");
}
}, 2000);
});
}
fetchData()
.then((data) => {
console.log(data);
console.log("Xử lý dữ liệu...");
})
.catch((error) => {
console.error(error);
});
// Output (nếu thành công):
// Dữ liệu đã được tải
// Xử lý dữ liệu...
Ưu điểm của Promise
- Giải quyết vấn đề Callback Hell.
- Dễ dàng xử lý lỗi với
.catch()
.
Async/Await
Async/Await là gì?
Async/Await là cú pháp được giới thiệu trong ES2017 (ES8) để làm việc với Promise một cách dễ đọc hơn. async
biến một hàm thành bất đồng bộ, và await
tạm dừng việc thực thi cho đến khi Promise hoàn thành.
Ví dụ về Async/Await
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // Thay đổi giá trị này để kiểm tra
if (success) {
resolve("Dữ liệu đã được tải");
} else {
reject("Lỗi khi tải dữ liệu");
}
}, 2000);
});
}
async function processData() {
try {
const data = await fetchData();
console.log(data);
console.log("Xử lý dữ liệu...");
} catch (error) {
console.error(error);
}
}
processData();
// Output (nếu thành công):
// Dữ liệu đã được tải
// Xử lý dữ liệu...
Ưu điểm của Async/Await
- Mã nguồn dễ đọc và bảo trì hơn.
- Xử lý lỗi dễ dàng với
try...catch
.
So sánh Callback, Promise và Async/Await
Đặc điểm | Callback | Promise | Async/Await |
---|---|---|---|
Dễ đọc | Khó (Callback Hell) | Trung bình | Dễ |
Xử lý lỗi | Phức tạp | Dễ với .catch() |
Dễ với try...catch |
Hỗ trợ trong ES | ES5 | ES6 | ES8 |
Tính năng nâng cao | Không | Có (chaining) | Có (dễ đọc hơn Promise) |
Event Loop trong JavaScript
Để hiểu rõ hơn về bất đồng bộ, bạn cần biết về Event Loop. Đây là cơ chế giúp JavaScript xử lý các tác vụ bất đồng bộ.
Cách hoạt động của Event Loop
- Call Stack: Nơi lưu trữ các hàm đang được thực thi.
- Web APIs: Nơi xử lý các tác vụ bất đồng bộ như
setTimeout
, HTTP request. - Task Queue: Nơi lưu trữ các callback chờ được đưa vào Call Stack.
Ví dụ minh họa
console.log("Bắt đầu");
setTimeout(() => {
console.log("Tác vụ bất đồng bộ");
}, 2000);
console.log("Kết thúc");
// Output:
// Bắt đầu
// Kết thúc
// Tác vụ bất đồng bộ
Kết luận
Hiểu rõ về bất đồng bộ trong JavaScript là rất quan trọng để xây dựng các ứng dụng hiệu quả và mạnh mẽ. Từ Callback, Promise, đến Async/Await, mỗi phương pháp đều có ưu và nhược điểm riêng. Hãy thực hành để nắm vững các khái niệm này và áp dụng chúng vào dự án của bạn.
Nếu bạn có bất kỳ câu hỏi nào, hãy để lại bình luận bên dưới!