JavaScript中的Event Loop与宏任务/微任务
字数 1233 2025-11-04 20:48:21
JavaScript中的Event Loop与宏任务/微任务
描述
Event Loop(事件循环)是JavaScript实现异步编程的核心机制,负责协调调用栈、Web API、任务队列之间的工作。宏任务(MacroTask)和微任务(MicroTask)是任务队列中的两种类型,它们的执行优先级和时机直接影响异步代码的顺序。理解二者的区别能避免实际开发中的执行顺序陷阱。
解题过程
-
基本概念
- 调用栈(Call Stack):同步代码按顺序执行的地方,当函数被调用时入栈,执行完毕出栈。
- Web API:浏览器提供的异步功能(如
setTimeout、AJAX),由其他线程处理,完成后将回调函数推入任务队列。 - 事件循环:持续检查调用栈是否为空,若为空则从任务队列中取出第一个任务推入调用栈执行。
-
宏任务与微任务的定义
- 宏任务:包括整体脚本、
setTimeout、setInterval、I/O操作、UI渲染等。 - 微任务:包括
Promise.then/catch/finally、MutationObserver、queueMicrotask等。 - 关键规则:
- 每执行一个宏任务后,会清空当前微任务队列中的所有任务。
- 微任务优先级高于宏任务。
- 宏任务:包括整体脚本、
-
执行流程示例分析
通过代码逐步演示执行顺序:console.log("1"); // 同步代码,直接输出 setTimeout(() => { console.log("2"); // 宏任务,进入宏任务队列 }, 0); Promise.resolve().then(() => { console.log("3"); // 微任务,进入微任务队列 }); console.log("4"); // 同步代码,直接输出步骤分解:
- 同步阶段:
- 输出
1→ 调用栈执行console.log("1")后清空。 - 遇到
setTimeout,将其回调函数交给Web API计时(0ms后放入宏任务队列)。 - 遇到
Promise.resolve().then,将回调函数放入微任务队列。 - 输出
4→ 调用栈清空。
- 输出
- 微任务阶段:
调用栈为空后,事件循环先检查微任务队列,发现Promise回调,输出3。 - 宏任务阶段:
微任务队列清空后,从宏任务队列中取出setTimeout回调,输出2。
最终输出顺序:1 → 4 → 3 → 2。
- 同步阶段:
-
嵌套场景下的复杂案例
setTimeout(() => console.log("宏任务1"), 0); Promise.resolve().then(() => { console.log("微任务1"); Promise.resolve().then(() => console.log("微任务2")); }); queueMicrotask(() => console.log("微任务3"));执行顺序:
- 同步代码执行:注册宏任务1到队列,注册
Promise.then(微任务1)到微任务队列,注册queueMicrotask(微任务3)到微任务队列。 - 清空微任务队列:按注册顺序依次执行微任务1 → 输出后嵌套的
Promise.then(微任务2)被加入当前微任务队列,继续清空微任务队列(微任务3 → 微任务2)。 - 最后执行宏任务1。
输出顺序:微任务1 → 微任务3 → 微任务2 → 宏任务1。
- 同步代码执行:注册宏任务1到队列,注册
-
总结与注意事项
- 微任务队列在每个宏任务执行后清空,且期间新产生的微任务会继续加入当前队列并执行(递归清空)。
- 宏任务按事件循环的轮次执行,例如
setTimeout的延迟是相对于当前宏任务完成后的最小时间。 - 常见误区:
setTimeout(fn, 0)并非立即执行,而是等待当前同步代码及微任务队列清空后才会执行。