深入理解 JavaScript 事件循环:Promise 与 setTimeout 谁先执行?

一、前言

在 JavaScript 的异步编程中,事件循环(Event Loop) 决定了代码的执行顺序。Promise 和 setTimeout 作为常见的异步操作,很多人容易混淆它们的执行顺序。

今天,我们通过一段代码深入解析 JavaScript 事件循环的执行机制,并彻底搞懂 同步任务、微任务、宏任务 的调度规则!

二、代码示例:Promise + setTimeout,谁先执行?

ts 复制代码
setTimeout(() => {
  console.log(1)
}, 0)

const p = new Promise((resolve, reject) => {
  console.log(2)
  resolve(3)
  Promise.resolve(4).then(console.log)
  console.log(5)
}).then(console.log)

console.log(6)

在不运行代码的情况下,你能猜出它的输出顺序吗? (建议先思考一下,再往下看解析 🤔)

三、代码执行流程详解

3.1、执行同步代码

JavaScript 会先执行所有 同步任务 ,然后再处理 异步任务(微任务 > 宏任务)

  • setTimeout (宏任务) 只是被注册,不会立即执行,而是放入 宏任务队列
  • Promise 构造函数内的代码 是同步执行的
    • console.log(2) 同步执行 ,输出 2
    • resolve(3) 只是改变 Promise 状态 ,但 .then(console.log) 不会立刻执行
    • Promise.resolve(4).then(console.log) 创建了一个微任务 ,加入 微任务队列
    • console.log(5) 同步执行 ,输出 5
  • console.log(6) 同步执行 ,输出 6

3.2、执行微任务队列(Microtask Queue)

所有 同步代码执行完毕 后,JavaScript 进入 微任务阶段,开始执行微任务队列中的任务。

  • Promise.resolve(4).then(console.log) 先执行,输出 4
  • resolve(3).then(console.log) 执行,输出 3

3.3、执行宏任务队列(Macrotask Queue)

所有 微任务执行完毕 后,JavaScript 进入 宏任务阶段 ,执行 任务队列中的宏任务

  • setTimeout 任务被执行,输出 1

四、最终执行顺序

ts 复制代码
setTimeout(() => {
  console.log(1)
}, 0)  // 宏任务(MacroTask),延迟执行,放入任务队列

const p = new Promise((resolve, reject) => {
  console.log(2)  // 立即执行(同步代码)
  resolve(3)  // 将 `3` 作为 Promise 的解决值
  Promise.resolve(4).then(console.log)  // 微任务,then 立即进入微任务队列
  console.log(5)  // 继续执行同步代码
}).then(console.log)  // `resolve(3)` 触发的 then,进入微任务队列

console.log(6)  // 继续执行同步代码
  • 2 // Promise 内同步执行
  • 5 // Promise 内同步执行
  • 6 // 主线程同步执行
  • 4 // 微任务:Promise.resolve(4).then()
  • 3 // 微任务:resolve(3).then()
  • 1 // 宏任务:setTimeout

五、JavaScript 事件循环(Event Loop)核心规则

  • 先执行所有同步代码 ,这些代码会直接进入 调用栈(Call Stack) 立即执行
  • 遇到异步任务时
    • 微任务(Microtask) (Promise.then、MutationObserver、queueMicrotask)进入 微任务队列
    • 宏任务(Macrotask) (setTimeout、setInterval、setImmediate、I/O 操作、UI 渲染)进入 宏任务队列
  • 同步代码执行完后
    • 先清空微任务队列(按照先进先出的顺序执行)
    • 再执行宏任务队列中的任务(setTimeout 等)
  • 进入下一个事件循环,重复以上过程

六、总结

  • 同步代码最先执行
  • Promise.then(微任务)优先于 setTimeout(宏任务)
  • 微任务的执行顺序遵循先进先出(FIFO)
  • JavaScript 采用单线程机制,但通过事件循环处理异步任务

七、思考题 🤔

阅读完这篇文章后,你能正确判断下面代码的输出顺序吗?

ts 复制代码
console.log('A')

setTimeout(() => {
  console.log('B')
}, 0)

Promise.resolve()
  .then(() => {
    console.log('C')
  })
  .then(() => {
    console.log('D')
  })

console.log('E')

在评论区留下你的答案,并告诉我你的思考过程吧!🚀

希望这篇文章能帮你彻底理解 JavaScript 事件循环,欢迎点赞 👍、收藏 ⭐、留言 💬 交流!

相关推荐
子兮曰20 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰20 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万20 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝20 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋21 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁21 小时前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95271 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大1 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师1 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学1 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端