彻底搞懂 Event Loop!这篇文章帮你一次性吃透宏任务、微任务、执行顺序

Event Loop(事件循环) 是 JavaScript 执行机制里的核心知识点。不管是前端面试还是日常开发,只要涉及异步,就绕不开它。

这篇文章不玩高深术语,用一套真实示例,带你搞懂:

  • 同步任务 、宏任务 、微任务 到底是什么
  • 它们的执行顺序和优先级
  • 最后特别提醒:Promise.resolve() 和 .then() 到底哪个才是微任务?

🧵 为什么需要 Event Loop?

JS 是单线程,意味着同一时刻只能执行一件事。

比如:

js 复制代码
console.log('A');
console.log('B');
console.log('C');

执行结果必然是:

css 复制代码
A
B
C

这就是同步任务:顺序执行,执行完才往下走。

但是,现实开发中需要执行很多耗时任务(比如网络请求、定时器)。如果都用同步执行,页面直接卡死,体验没法要。

所以浏览器和 Node 设计了异步队列 + Event Loop,先把耗时任务挂起,空闲时再回头执行。


🗂️ 同步任务、宏任务、微任务,怎么分?

先看个对比表:

类型 代表
同步任务 普通函数、console.log、函数调用
宏任务(Macrotask) 整个 <script>、setTimeout、setInterval、setImmediate、MessageChannel(浏览器)、I/O(Node)
微任务(Microtask) Promise.then()、MutationObserver、queueMicrotask、process.nextTick(Node 专属)

执行顺序口诀:

一个宏任务 → 执行同步任务 → 清空微任务 → 执行下一个宏任务 → 循环


✅ 浏览器执行顺序经典示例

来看个最典型的组合拳:

js 复制代码
console.log('代码开始');

setTimeout(() => {
  console.log('这是 setTimeout 回调');
}, 0);

Promise.resolve().then(() => {
  console.log('这是 promise 的回调');
});

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

🧐 输出结果:

arduino 复制代码
代码开始
代码结束
这是 promise 的回调
这是 setTimeout 回调

执行过程拆解:

  1. <script> 整个文件就是一个宏任务,先跑同步代码。
  2. setTimeout 是下一个宏任务,排到队列后面。
  3. Promise.then 是微任务,会在本轮宏任务跑完后立刻执行。
  4. 所以输出顺序是:同步 → 微任务 → 下一个宏任务

❗ 重点:Promise.resolve() 本身不是微任务!

超多人搞错,以为 Promise.resolve() 本身是微任务,其实不是!

来看个例子:

js 复制代码
console.log('程序启动');

const p1 = Promise.resolve('第一个 Promise');
const p2 = Promise.resolve('第二个 Promise');
const p3 = new Promise(resolve => {
    console.log('执行 p3 构造函数');
    resolve('第三个 Promise');
});

p1.then(value => console.log(value));
p2.then(value => console.log(value));
p3.then(value => console.log(value));

setTimeout(() => {
    console.log('第一波 setTimeout');
    const p4 = Promise.resolve('第四个 Promise');
    p4.then(value => console.log(value));
}, 0);

setTimeout(() => {
    console.log('第二波 setTimeout');
}, 0);

console.log('程序结束');

执行顺序:

javascript 复制代码
程序启动
执行 p3 构造函数
程序结束
第一个 Promise
第二个 Promise
第三个 Promise
第一波 setTimeout
第四个 Promise
第二波 setTimeout

🔑 为什么?

  • Promise.resolve() 只是返回一个已确定状态的 Promise,不会产生微任务。

  • 真正触发微任务队列的是 .then() 的回调!

  • 所以这段里:

    • 同步任务先跑完:程序启动 → 执行 p3 构造函数 → 程序结束
    • 再清空微任务队列:第一个 Promise、第二个 Promise、第三个 Promise
    • 然后执行第一个 setTimeout(宏任务):第一波 setTimeout,里面的 .then() 会生成一个新的微任务:第四个 Promise
    • 最后执行第二个 setTimeout:第二波 setTimeout

🧬 MutationObserver:监听 DOM 改变的微任务

浏览器里,MutationObserver 也属于微任务。看个示例:

js 复制代码
const target = document.createElement('div');
document.body.appendChild(target);

const observer = new MutationObserver(() => {
  console.log('微任务: MutationObserver 回调执行');
});

observer.observe(target, { attributes: true, childList: true });

target.setAttribute('data-role', '123');
target.appendChild(document.createElement('span'));

这里修改了 target 的属性和子元素,MutationObserver 会把回调放进微任务队列,在本轮宏任务执行完后跑。


⚡ Node.js 专属:process.nextTick 比普通微任务还快!

Node.js 里还有个专属:process.nextTick,它优先级比普通微任务还高。

js 复制代码
console.log('启动');

process.nextTick(() => {
  console.log('执行 process.nextTick');
});

Promise.resolve().then(() => {
  console.log('执行 Promise.then');
});

setTimeout(() => {
  console.log('执行 setTimeout');
  Promise.resolve().then(() => {
    console.log('setTimeout 中的 Promise');
  });
}, 0);

console.log('收尾');

🧐 输出结果:

javascript 复制代码
启动
收尾
执行 process.nextTick
执行 Promise.then
执行 setTimeout
setTimeout 中的 Promise

✏️ queueMicrotask:手动插个微任务

需要自己把任务插进微任务队列?用 queueMicrotask!

js 复制代码
console.log('准备执行');

queueMicrotask(() => {
  console.log('这是 queueMicrotask 回调');
});

console.log('执行完同步任务');

输出:

复制代码
准备执行
执行完同步任务
这是 queueMicrotask 回调

和 Promise.then 一样效果,都会在本轮宏任务执行完后立刻跑。


🗂️ 总结:执行顺序记住这张表

优先级 队列
🥇 最高 process.nextTick(Node 专属)
🥈 第二 Promise.then、MutationObserver、queueMicrotask
🥉 最后 宏任务:setTimeout、setInterval、I/O

顺序一定是:

  • 当前宏任务执行完 → 清空所有微任务
  • 再执行下一个宏任务
  • 循环往复

🚀 最后的金句

Promise.resolve() 只是创建了一个已确定状态的 Promise,本身不是微任务。只有调用 .then() 后,回调才会排进微任务队列!

别再搞混了!

面试题里很多顺序题,考点就是这里。

相关推荐
Ai-_Man14 分钟前
您您这可以把Grok的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。AI导出鸭
javascript·人工智能·ai·小程序·电脑
传人once17 分钟前
多图片上传预览功能如何写
前端·css·html·jquery·html5
Eric.4638 分钟前
Wan‑Animate+MiniMax-H3 本地部署 AI 漫剧流水线:8G 显存动作迁移与动态分镜工程实战
前端·人工智能·comfyui·ai漫剧
liangshanbo12151 小时前
JavaScript 异步核心主线
javascript·事件循环·异步操作
传人once2 小时前
悬停旋转放大和位移效果如何写
javascript·css·动画
库拉镜像AI牛牛2 小时前
短剧内容自动化生产:知漫剧工作室落地教程
大数据·服务器·前端·人工智能·语音识别
律宏阔2 小时前
Flutter 列表图片内存优化:CachedNetworkImage 与 ClipRRect 的取舍
前端·flutter
行者全栈架构师2 小时前
【鸿蒙心迹】相册文搜图工程化落地:HarmonyOS 7.0 端侧 AI 检索全链路实战与 5 个踩坑解法
前端
律宏阔2 小时前
Dart 3 Record:解决 Future.wait 异构返回值的类型问题
前端·flutter
Seraphina363 小时前
DVWA(XSS Reflected/Stored High,CSRF Low)
前端·xss·csrf