1.1 Các khái niệm cơ bản về bất đồng bộ
Bất đồng bộ trong JavaScript cho phép thực hiện các tác vụ ở chế độ nền mà không làm gián đoạn luồng thực thi chính. Điều này rất quan trọng cho các tác vụ có thể mất nhiều thời gian, chẳng hạn như yêu cầu mạng, đọc tệp và bộ đếm thời gian. Bây giờ chúng ta sẽ xem xét các khái niệm chính của lập trình bất đồng bộ trong JavaScript và đưa ra các ví dụ sử dụng.
JavaScript là ngôn ngữ đơn luồng
JavaScript là ngôn ngữ đơn luồng: điều này có nghĩa là nó thực hiện mã tuần tự trong một luồng duy nhất. Tuy nhiên, các hoạt động bất đồng bộ cho phép thực hiện các tác vụ ở chế độ nền, giải phóng luồng chính cho các tác vụ khác.
Event Loop
Event Loop là cơ chế quan trọng cho phép JavaScript xử lý các tác vụ bất đồng bộ. Event Loop quản lý hàng đợi thông điệp (Message Queue) và hàng đợi vi tác vụ (Microtask Queue), đảm bảo thực hiện các hoạt động bất đồng bộ.
- Hàng đợi thông điệp: chứa các tác vụ, chẳng hạn như xử lý sự kiện, yêu cầu mạng và bộ đếm thời gian. Các tác vụ từ hàng đợi này được thực hiện tuần tự.
- Hàng đợi vi tác vụ: chứa các tác vụ có ưu tiên cao hơn so với các tác vụ trong hàng đợi thông điệp. Ví dụ bao gồm hoàn thành promise và các cuộc gọi lại (callbacks) trong vi tác vụ.
Event Loop liên tục kiểm tra cả hai hàng đợi và thực hiện các tác vụ từ đó khi luồng chính trở nên trống.
Các hoạt động bất đồng bộ
Các hoạt động bất đồng bộ cho phép thực hiện các tác vụ ở chế độ nền. Các ví dụ chính về hoạt động bất đồng bộ bao gồm:
- Bộ đếm thời gian (
setTimeout,setInterval) - Xử lý sự kiện
- Yêu cầu mạng (ví dụ,
XMLHttpRequest,Fetch API) - Đọc/ghi tệp (trong Node.js)
Hãy cùng xem một vài ví dụ về hoạt động bất đồng bộ.
1.2 Bộ đếm thời gian
Bộ đếm thời gian cho phép thực hiện các tác vụ bị trì hoãn hoặc cách đều nhau theo khoảng thời gian.
Ví dụ sử dụng setTimeout
Trong ví dụ này, setTimeout thiết lập thực thi hàm sau 2 giây. Kết quả là, Start và End được in ra trước, rồi sau 2 giây Executed after 2 seconds được in ra.
console.log('Start');
setTimeout(() => {
console.log('Executed after 2 seconds');
}, 2000);
console.log('End');
Ví dụ sử dụng setInterval
Trong ví dụ này, setInterval thực hiện hàm mỗi giây, tăng dần counter và in ra giá trị của nó. Khi counter đạt 5, khoảng thời gian được xóa bằng cách sử dụng clearInterval:
let counter = 0;
const intervalID = setInterval(() => {
counter++;
console.log(`Counter: ${counter}`);
if (counter >= 5) {
clearInterval(intervalID);
}
}, 1000);
1.3 Xử lý sự kiện
Xử lý sự kiện cho phép thực hiện mã phản hồi lại hành động của người dùng hoặc các sự kiện khác.
Ví dụ sử dụng xử lý sự kiện
Trong ví dụ này, một bộ xử lý sự kiện click được thêm vào nút. Khi người dùng nhấp vào nút, thông báo Button clicked! được in ra:
<!DOCTYPE html>
<html>
<head>
<title>Event Handler Example</title>
</head>
<body>
<button id="myButton">Click me</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
});
</script>
</body>
</html>
1.4 Yêu cầu mạng
Yêu cầu mạng cho phép thực hiện các yêu cầu HTTP bất đồng bộ đến máy chủ.
Ví dụ sử dụng XMLHttpRequest
Trong ví dụ này, một yêu cầu GET bất đồng bộ được tạo ra đến một API, và khi yêu cầu hoàn thành, phản hồi được in ra trong console:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log(response);
}
};
xhr.send();
1.5 Event Loop trong thực tế
Để hiểu rõ hơn về cách hoạt động của Event Loop, hãy xem xét ví dụ sau:
console.log('Start');
setTimeout(() => {
console.log('Timeout 1');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 1');
});
setTimeout(() => {
console.log('Timeout 2');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 2');
});
console.log('End');
Kết quả dự kiến sẽ là:
StartEndPromise 1Promise 2Timeout 1Timeout 2
Trong ví dụ này, đầu tiên các hoạt động đồng bộ được thực hiện (console.log('Start') và console.log('End')). Sau đó các vi tác vụ được thực hiện (trình xử lý promise), và chỉ sau đó các tác vụ từ hàng đợi thông điệp (setTimeout) được thực hiện.
GO TO FULL VERSION