JavaScript 的事件循环机制是其单线程执行模型的核心。以下是事件循环的详细步骤:
事件循环的步骤
所有同步任务都在主线程上执行,形成一个执行栈(Execution Context Stack):
- JavaScript 中所有的代码都是在一个单一的主线程上执行的。
- 同步任务会按照顺序依次压入执行栈中,执行完毕后依次弹出。
主线程之外,还存在一个任务队列(Task Queue):
- 任务队列用于存放所有需要在未来某个时刻执行的异步任务。
- 任务队列分为两种:宏任务队列(Macro Task Queue)和微任务队列(Micro Task Queue)。
当主线程上的所有同步任务执行完毕后,主线程会清空执行栈:
- 同步任务执行完毕后,执行栈会被清空,表示当前的同步代码已经执行完毕。
然后读取任务队列中的第一个任务:
- 事件循环会从宏任务队列中读取第一个任务,并将其放入执行栈中执行。
每一个宏任务执行完毕后,都要清空所有的微任务:
- 在每个宏任务结束后,事件循环会检查微任务队列,并按照顺序执行所有微任务。
- 微任务执行完毕后,事件循环才会继续处理下一个宏任务。
重复上述过程:
- 事件循环不断重复上述过程,确保异步任务被及时处理,同时主线程保持畅通。
具体流程
全局代码执行:
- 全局代码会被当作一个宏任务,压入执行栈中。
- 在执行全局代码的过程中,如果遇到异步任务(例如
setTimeout
、Promise
等),会将其回调函数放入对应的任务队列中。
事件循环开始:
- 全局代码执行完毕后,事件循环开始。
- 从宏任务队列中取出第一个宏任务,执行它。
微任务执行:
- 当前宏任务执行完毕后,事件循环会检查微任务队列,并执行所有微任务。
下一个宏任务执行:
- 微任务执行完毕后,事件循环会从宏任务队列中取出下一个宏任务,继续执行。
示例代码
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
执行过程:
同步任务执行:
- 输出
script start
。 setTimeout
回调放入宏任务队列。Promise
第一个.then
回调放入微任务队列。Promise
第二个.then
回调放入微任务队列。- 输出
script end
。
- 输出
全局代码执行完毕:
- 执行栈清空。
执行微任务:
- 执行第一个微任务,输出
promise1
。 - 执行第二个微任务,输出
promise2
。
- 执行第一个微任务,输出
执行宏任务:
- 执行
setTimeout
回调,输出setTimeout
。
- 执行
最终输出顺序:
script start
script end
promise1
promise2
setTimeout
总结
- 执行栈(Execution Context Stack):所有同步任务都在主线程上执行,形成一个执行栈。
- 任务队列(Task Queue):包含宏任务队列和微任务队列,用于存放异步任务的回调。
- 事件循环(Event Loop):负责在执行栈为空时,从任务队列中取出任务执行,确保异步任务被及时处理。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。