Trong phần này, chúng ta sẽ tiếp tục tìm hiểu về xử lý bất đồng bộ trong JavaScript bằng cách sử dụng Promise và Async/Await để đơn giản hóa mã nguồn và tối ưu hoá thời gian chạy.
Tổng Quan
Sau phần 1 về xử lý bất đồng bộ trong JavaScript, chúng ta sẽ đi vào các phương pháp cụ thể hơn. Ở đây, chúng ta sẽ thực hiện ví dụ sử dụng Promise và Async/Await.
Lý Thuyết Chưa Được Đề Cập
Điều đầu tiên chúng ta cần làm là xem xét lý thuyết về Promise và Async/Await. Bạn có thể tham khảo tài liệu chính thức hoặc hỏi trực tiếp ChatGPT để có thêm thông tin chi tiết.
Các Ví Dụ Cụ Thể
Chúng ta sẽ sử dụng những ví dụ đã trình bày ở phần trước nhưng lần này sẽ thay thế tất cả các hàm callback bằng Promise và Async/Await để cải thiện cách tổ chức mã.
Đầu tiên, chúng ta sẽ sử dụng hàm delay
để giả lập thời gian thực thi:
javascript
function delay(x) {
const start = new Date().getTime();
while (new Date().getTime() - start < x * 1000) {}
}
Tiếp theo là các hàm mô tả quy trình sơ chế và luộc rau:
javascript
function soCheRau() {
delay(3);
console.log('Sơ chế rau.');
}
function dunSoiNuoc() {
delay(4);
console.log('Đun sôi nước.');
}
function luocRau() {
delay(5);
console.log('Luộc rau.');
}
function votRau() {
delay(3);
console.log('Vớt rau, để nguội.');
}
Nếu để các hàm chạy tuần tự sẽ mất tổng thời gian lên tới khoảng 15 giây. Mục tiêu của chúng ta là tối ưu thời gian này xuống khoảng 12 giây mà vẫn đảm bảo trình tự hợp lý trong các bước thực hiện.
Tối Ưu Thời Gian Chạy
Để rút gọn thời gian, chúng ta có thể chạy hai hàm cùng một lúc. Trong trường hợp này, cả hai hàm soCheRau()
và dunSoiNuoc()
có thể thực hiện song song:
javascript
console.time('run');
Promise.all([soCheRau(), dunSoiNuoc()])
.then(() => {
luocRau();
votRau();
})
.then(() => {
console.timeEnd('run');
});
Khi chúng ta thử chạy đoạn mã này, kết quả vẫn cho thấy thời gian thực thi gần 15 giây. Vấn đề ở đây là vì các hàm của chúng ta đều là hàm đồng bộ (sync) chứ không phải hàm bất đồng bộ (async). Vì vậy, chúng ta cần chuyển đổi các hàm trên thành các hàm async và trả về một Promise:
javascript
function soCheRau() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Sơ chế rau.');
resolve();
}, 3000);
});
}
Chúng ta cũng có thể viết lại hàm delay
thành hàm async:
javascript
function delay(x) {
return new Promise(resolve => setTimeout(resolve, x * 1000));
}
Hoàn Thành Cú Pháp Mới
Sau khi sửa đổi các hàm thành hàm async, chúng ta có thể viết lại mã như sau:
javascript
console.time('run');
Promise.all([soCheRau(), dunSoiNuoc()])
.then(() => {
return luocRau();
})
.then(() => {
return votRau();
})
.then(() => {
console.timeEnd('run');
});
Khi sử dụng Promise Chaining, mỗi lần sử dụng .then
chúng ta cần đảm bảo có return
để trả về một giá trị, điều này có thể gây vấn đề nếu chúng ta quên.
Sử Dụng Async/Await
Chúng ta có thể viết lại mã theo cú pháp Async/Await để ngắn gọn và dễ đọc hơn:
javascript
async function start() {
console.time('run');
await Promise.all([soCheRau(), dunSoiNuoc()]);
await luocRau();
await votRau();
console.timeEnd('run');
}
start();
Xử Lý Lỗi
Xử lý lỗi là phần cực kỳ quan trọng khi phát triển phần mềm. Nếu trong quá trình thực hiện xảy ra lỗi, như thất thoát nước trong hàm ruaRau
, chúng ta cần có một cơ chế để quản lý lỗi:
javascript
async function ruaRau() {
console.log('Bắt đầu rửa rau.');
await delay(2);
throw Error('Mất nước!');
}
Nếu không xử lý lỗi, chương trình sẽ ngừng hoạt động. Chúng ta có thể dùng try...catch
để quản lý lỗi:
javascript
async function soCheRau() {
await vatRau();
try {
await ruaRau();
} catch (error) {
console.log(error);
}
console.log('Sơ chế rau xong.');
}
Bài Học Rút Ra
- Khi sử dụng Promise hoặc Async/Await nên cẩn thận về tính đồng bộ và bất đồng bộ của hàm.
- Hãy nhớ rằng không phải cứ sử dụng từ khóa
async
thì hàm đó đều là hàm async. - Đừng quên xử lý lỗi để đảm bảo chương trình luôn ổn định.
Nếu bạn muốn tìm hiểu thêm, đừng ngần ngại bình luận dưới bài viết hoặc hỏi thêm trên các nguồn tài liệu khác.
Happy Coding 😁.
source: viblo