工作原理
JavaScript 事件循环模型是基于单线程的执行机制。它使用事件队列(Event Queue)和调用栈(Call Stack)来管理和执行任务。
- 当 JavaScript 引擎执行同步任务时,它会将这些任务按照顺序放入调用栈中执行。
- 当遇到异步任务时(如定时器、网络请求、事件监听等),引擎会将这些任务交给相应的 Web API 处理,并注册回调函数。
- 当异步任务完成并准备好被执行时,它会被添加到事件队列中。
- 当调用栈为空时,JavaScript 引擎会检查事件队列,如果队列中有任务,则将任务从队列中取出并放入调用栈中执行。
这个过程不断循环,被称为事件循环。通过事件循环模型,JavaScript 可以实现非阻塞的异步操作,使得程序可以同时处理多个任务。
组成部分
JavaScript 事件循环模型由以下几个组成部分构成:
1. 调用栈(Call Stack)
调用栈是用于存储执行上下文(Execution Context)的数据结构。它遵循后进先出(LIFO)的原则。当函数被调用时,会将其执行上下文压入调用栈顶部,当函数执行完成后,会将其执行上下文从调用栈中弹出。调用栈用于处理同步任务。
2. 事件队列(Event Queue)
事件队列用于存储异步任务的回调函数。当异步任务完成后,其回调函数会被添加到事件队列中。事件队列采用先进先出(FIFO)的原则,即先进入队列的任务会先被取出执行。
3. Web API
Web API 是浏览器提供的一组 API,用于处理各种异步任务,比如定时器、网络请求、DOM 事件等。当引擎遇到异步任务时,会将其委托给相应的 Web API 处理。一旦异步任务完成,Web API 会将回调函数放入事件队列中。
4. 事件循环(Event Loop)
事件循环是 JavaScript 引擎的核心部分。它负责不断地检查调用栈和事件队列,当调用栈为空时,会从事件队列中取出任务并放入调用栈中执行。
示例
下面是一个简单的示例:
代码语言:javascript复制console.log('Start');
setTimeout(function() {
console.log('Timeout');
}, 0);
Promise.resolve().then(function() {
console.log('Promise');
});
console.log('End');
在上面的示例代码中,我们有一个同步任务和两个异步任务。首先,我们输出 'Start'
。
然后,我们使用 setTimeout
函数创建一个定时器,设置超时时间为 0 毫秒。即使超时时间为 0,它仍被认为是一个异步任务。回调函数 'Timeout'
被注册,并被委托给浏览器的定时器 Web API 来处理。
接下来,我们使用 Promise.resolve().then()
创建一个 Promise 对象,并注册回调函数 'Promise'
。Promise 对象是另一个异步任务,回调函数会被委托给浏览器的 Promise Web API 来处理。
最后,我们输出 'End'
。
代码执行过程如下:
- 执行同步任务,输出
'Start'
。 - 调用
setTimeout
,将回调函数添加到事件队列中,并委托给浏览器的定时器 Web API 处理。 - 执行
Promise.resolve().then()
,将回调函数添加到事件队列中,并委托给浏览器的 Promise Web API 处理。 - 输出
'End'
。 - 调用栈为空,事件循环开始。
- 事件循环检查事件队列,发现定时器任务,将其放入调用栈中执行,输出
'Timeout'
。 - 定时器任务执行完成,调用栈为空,事件循环继续。
- 事件循环检查事件队列,发现 Promise 任务,将其放入调用栈中执行,输出
'Promise'
。 - Promise 任务执行完成,调用栈为空,事件循环继续。
- 事件循环检查事件队列,发现没有任务,结束。
结果输出为:
代码语言:javascript复制Start
End
Promise
Timeout
通过事件循环模型,JavaScript 可以在执行同步任务的同时处理异步任务,实现非阻塞的异步操作。每个任务都按照其注册的顺序执行,保证了代码的可预测性和顺序性。