1.1 非同步的主要概念
JavaScript 的非同步性讓你能在背景中執行任務,而不會阻塞主執行緒。這對於可能需要花費大量時間的任務特別重要,例如網絡請求、文件讀取和計時器。現在我們來看看 JavaScript 非同步編程的基本概念並給出使用範例。
JavaScript 的單執行緒特性
JavaScript 是單執行緒的,這意味著它會在一個執行緒中順序執行代碼。然而,非同步操作允許在背景中執行任務,釋放主執行緒以進行其他任務。
Event Loop(事件循環)
事件循環(Event Loop)是 JavaScript 處理非同步任務的關鍵機制。事件循環管理消息隊列(Message Queue)和微任務隊列(Microtask Queue),確保非同步操作的執行。
- 消息隊列:包含任務,如事件處理器、網絡請求和計時器。這些任務依序執行。
- 微任務隊列:包含優先級較高的任務,相較於消息隊列中的任務。示例包括 promise 的完成和微任務中的回調函數(callbacks)調用。
事件循環不斷檢查兩個隊列,並當主執行緒變空閒時從中執行任務。
非同步操作
非同步操作允許在背景中執行任務。主要的非同步操作示例包括:
- 計時器 (
setTimeout,setInterval) - 事件處理器
- 網絡請求(例如
XMLHttpRequest,Fetch API) - 文件讀寫(在 Node.js 中)
讓我們來看看一些非同步操作的例子。
1.2 計時器
計時器允許你以延遲或定期的時間間隔執行任務。
setTimeout 使用示例
在這個例子中,setTimeout 設定了函數在 2 秒後執行。因此,先輸出 Start 和 End,然後在 2 秒後輸出 Executed after 2 seconds。
console.log('Start');
setTimeout(() => {
console.log('Executed after 2 seconds');
}, 2000);
console.log('End');
setInterval 使用示例
在這個例子中,setInterval 每秒執行一次函數,遞增計數器並輸出其值。當計數器達到 5 時,使用 clearInterval 清除間隔:
let counter = 0;
const intervalID = setInterval(() => {
counter++;
console.log(`Counter: ${counter}`);
if (counter >= 5) {
clearInterval(intervalID);
}
}, 1000);
1.3 事件處理器
事件處理器允許你在用戶操作或其他事件發生時執行代碼。
事件處理器使用示例
在這個例子中,click 事件處理器被添加到按鈕。當用戶點擊按鈕時,會輸出訊息 Button clicked!:
<!DOCTYPE html>
<html>
<head>
<title>事件處理器範例</title>
</head>
<body>
<button id="myButton">點我</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
});
</script>
</body>
</html>
1.4 網絡請求
網絡請求允許你發送非同步 HTTP 請求到伺服器。
XMLHttpRequest 使用示例
在這個例子中,創建一個非同步 GET 請求到 API,當請求完成時,將響應輸出到控制台:
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 運作
為了更好理解 Event Loop,讓我們看看下面的例子:
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');
預期輸出如下:
StartEndPromise 1Promise 2Timeout 1Timeout 2
在這個例子中,首先執行同步操作(console.log('Start') 和 console.log('End'))。然後執行微任務(promise 處理器),之後才執行消息隊列中的任務(setTimeout)。
GO TO FULL VERSION