JavaScript 事件循环、微任务与宏任务:从原理到实践

132 阅读3分钟

引言*

在现代前端开发中,理解 JavaScript 的事件循环(Event Loop) 及其微任务(Microtask)和宏任务(Macrotask)的运行机制,是编写高效、响应式代码的关键。同时,它也是技术面试中的高频考点。本文将深入介绍这些概念,并展示其应用和优化策略。

1. 什么是事件循环?*

JavaScript 是一种 单线程 语言,也就是说,同一时间只能执行一个任务。为了实现异步处理,JavaScript 使用了 事件循环。事件循环由以下几部分组成:

调用栈(Call Stack) :同步任务按顺序进入调用栈执行。

宏任务队列(Macrotask Queue) :存放 setTimeout、setInterval、UI 事件等宏任务。

微任务队列(Microtask Queue) :存放 Promise 的回调、queueMicrotask() 等。

事件循环的工作流程

  1. 执行调用栈中的所有同步代码。

  2. 清空微任务队列中的所有任务。

  3. 从宏任务队列中取出一个任务执行,然后再次检查微任务队列。

2. 宏任务与微任务的区别*

宏任务(Macrotask)*

来源:setTimeout、setInterval、UI 事件、I/O 操作等。

执行规则:在每轮事件循环中,只执行一个宏任务,然后检查是否有微任务等待执行。

微任务(Microtask)*

来源:Promise.then()、MutationObserver、queueMicrotask()。

执行规则:每轮宏任务执行结束后,立即清空微任务队列中的所有任务。

3. 执行顺序示例*

以下示例代码展示了同步代码、微任务与宏任务的执行顺序:


console.log('同步代码开始');

setTimeout(() => {

  console.log('宏任务:setTimeout');

}, 0);

Promise.resolve()

  .then(() => {

    console.log('微任务:Promise.then');

  })

  .then(() => {

    console.log('微任务:Promise.then 链式调用');

  });

  

console.log('同步代码结束');

输出结果


同步代码开始

同步代码结束

微任务:Promise.then

微任务:Promise.then 链式调用

宏任务:setTimeout

解释

  1. 执行同步代码:同步代码开始 和 同步代码结束 直接输出。

  2. 清空微任务队列:执行 Promise.then 回调和链式调用。

  3. 执行宏任务队列:setTimeout 在所有微任务完成后执行。

4. async/await 与事件循环的关系*

async/await 本质上是基于 Promise 实现的,因此 await 后的代码会被加入微任务队列:

async function test() {

  console.log('async 函数开始');

  await Promise.resolve();

  console.log('await 后的代码');

}
test();

console.log('全局代码');

输出顺序


async 函数开始

全局代码

await 后的代码

解释:

• async 函数开始 和 全局代码 会立即执行。

• await 后的代码被放入 微任务队列,在本轮宏任务执行结束后立刻执行。

5. 性能优化与最佳实践*

  1. 控制微任务数量:过多的微任务会阻塞主线程,影响页面的响应性。

  2. 拆分计算密集任务:使用 setTimeout 将复杂任务分割为多个小任务,避免 UI 卡顿。

  3. 优先使用微任务处理关键逻辑:如用户输入后的表单验证,应使用微任务以确保及时响应。

6. 结论与总结*

微任务的优先级高于宏任务,确保关键任务能更早执行。

• 使用 async/await 时,需注意微任务的执行顺序,以避免逻辑错误。

• 合理拆分任务和选择任务类型,有助于提升应用性能。

7. 视觉化示例:事件循环示意图*

下图展示了 JavaScript 事件循环的执行流程,帮助你更直观地理解微任务和宏任务的执行顺序:

image.png

8. Call to Action*

觉得这篇文章有帮助吗?欢迎 点赞、评论 并分享给更多的开发者。如果你有任何疑问或心得,请在评论区留言讨论!

参考资料*

MDN Web Docs: JavaScript 运行时

Event Loop in JavaScript - javascript.info

掘金:深入理解事件循环