1.1 异步性的基本概念
在 JavaScript 中,异步性允许在后台执行任务,而不阻塞主执行线程。 这对于可能占用大量时间的任务特别重要,比如网络请求、文件读取和计时器。现在我们将讨论 JavaScript 中异步编程的基本概念,并举例说明其应用。
JavaScript 的单线程特性
JavaScript 是一种单线程语言:这意味着它按顺序在一个线程中执行代码。 然而,异步操作允许任务在后台执行,从而释放主线程以执行其他任务。
Event Loop (事件循环)
事件循环是一个关键机制,它允许 JavaScript 处理异步任务。 事件循环管理消息队列 (Message Queue) 和微任务队列 (Microtask Queue),确保异步操作的执行。
- 消息队列:包含任务,如事件处理程序、网络请求和计时器。这些队列中的任务按顺序执行。
- 微任务队列:包含比消息队列中的任务优先级更高的任务。示例包括 Promise 的完成和微任务中的回调函数调用。
事件循环不断检查这两个队列,并在主线程空闲时执行队列中的任务。
异步操作
异步操作允许在后台执行任务。主要的异步操作示例包括:
- 计时器 (
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>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 网络请求
网络请求允许向服务器发送异步 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');
预期输出如下:
Start
End
Promise 1
Promise 2
Timeout 1
Timeout 2
在这个示例中,首先执行的是同步操作 (console.log('Start')
和 console.log('End')
)。然后 执行的是微任务 (Promise 的处理程序),只有在此之后才执行消息队列中的任务 (setTimeout
)。
GO TO FULL VERSION