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.
Chưa có bình luận. Hãy là người đầu tiên!