Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Lấy dữ liệu từ máy chủ, đọc file, chờ một khoảng thời gian: những việc này mất thời gian, và JavaScript không muốn đứng im chờ vì giao diện sẽ bị đơ. Lập trình bất đồng bộ giải quyết điều đó. Promise và cú pháp async/await giúp bạn viết mã bất đồng bộ đọc như mã tuần tự thông thường.
Cách làm xưa là truyền một hàm callback sẽ được gọi khi việc xong. Khi nhiều bước phụ thuộc nhau, mã lồng sâu thành "callback hell":
layUser(function (user) {
layDonHang(user.id, function (donHang) {
layChiTiet(donHang[0].id, function (chiTiet) {
console.log(chiTiet);
});
});
});Khó đọc, và việc xử lý lỗi ở mỗi tầng rất vất vả.
Promise là một đối tượng đại diện cho kết quả sẽ có trong tương lai. Nó ở một trong ba trạng thái: đang chờ (pending), thành công (fulfilled) hoặc thất bại (rejected). Bạn tạo promise và xử lý như sau:
function doi(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
doi(1000)
.then(function () { console.log('Đã đợi 1 giây'); })
.catch(function (loi) { console.error(loi); })
.finally(function () { console.log('Xong'); });then nhận kết quả khi thành công, catch bắt lỗi, finally luôn chạy. Mỗi then trả về promise mới nên bạn nối chuỗi được, thay cho việc lồng nhau.
Từ khóa async trước hàm làm hàm luôn trả về promise. Bên trong, await tạm dừng hàm cho đến khi promise hoàn thành rồi trả về giá trị:
async function taiDuLieu() {
try {
const user = await layUser();
const donHang = await layDonHang(user.id);
const chiTiet = await layChiTiet(donHang[0].id);
console.log(chiTiet);
} catch (loi) {
console.error('Có lỗi:', loi);
} finally {
console.log('Kết thúc');
}
}So với callback, mã đọc từ trên xuống dưới, và một khối try/catch xử lý lỗi của mọi bước. Lưu ý await chỉ dùng được bên trong hàm async (hoặc ở cấp cao nhất của module).
Một sai lầm hiệu năng phổ biến là await tuần tự những việc không phụ thuộc nhau:
// Chậm: chạy lần lượt
const a = await layA();
const b = await layB();
// Nhanh hơn: chạy song song
const [ketQuaA, ketQuaB] = await Promise.all([layA(), layB()]);Promise.all chờ tất cả hoàn thành và thất bại ngay nếu một cái lỗi. Nếu muốn nhận kết quả của tất cả dù có cái lỗi, dùng Promise.allSettled. Còn Promise.race trả về kết quả của promise xong đầu tiên, hữu ích khi đặt thời gian chờ tối đa.
Promise { pending } khi in ra.forEach không chờ các hàm async. Hãy dùng vòng for...of nếu cần tuần tự, hoặc Promise.all với map nếu muốn song song.JavaScript chạy trên một luồng. Khi gặp việc bất đồng bộ như timer hoặc yêu cầu mạng, nó giao cho môi trường (trình duyệt) xử lý, rồi tiếp tục chạy mã khác. Khi việc xong, callback được xếp hàng đợi và chạy khi luồng rảnh. Callback của promise thuộc hàng đợi ưu tiên cao hơn timer. Đó là lý do setTimeout(fn, 0) vẫn chạy sau các then đã sẵn sàng.
Mạng không ổn định là chuyện thường. Một hàm thử lại vài lần trước khi bỏ cuộc là ví dụ tốt để luyện async/await:
const doi = (ms) => new Promise((r) => setTimeout(r, ms));
async function thuLai(tacVu, soLan = 3) {
for (let i = 1; i <= soLan; i++) {
try {
return await tacVu();
} catch (loi) {
if (i === soLan) throw loi;
await doi(500 * i); // chờ lâu dần sau mỗi lần lỗi
}
}
}
thuLai(() => fetch('/api/du-lieu').then(r => r.json()));Hàm này gọi lại tác vụ, tăng thời gian chờ sau mỗi lần thất bại và chỉ ném lỗi ở lần cuối. Hãy chỉ thử lại với những thao tác an toàn khi lặp lại, ví dụ lấy dữ liệu, không nên tự động thử lại việc thanh toán.
Promise biểu diễn kết quả tương lai, async/await giúp viết mã dễ đọc, try/catch xử lý lỗi gọn và Promise.all tăng tốc cho các việc độc lập. Hãy tập chuyển các đoạn dùng callback cũ sang async/await để thấy sự khác biệt.
Để áp dụng vào việc gọi API thực tế, bạn có thể tham khảo tiếp các khóa học trên codebem.com.
Nguyễn Đình Cường
0 comment