news 2026/6/15 17:49:19

宏任务与微任务:前端面试必考执行顺序

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
宏任务与微任务:前端面试必考执行顺序

宏任务 / 微任务执行顺序(经典面试题详解)

你关注的宏任务、微任务执行顺序是前端面试的核心考点,尤其围绕setTimeoutPromise.thenasync/await的执行逻辑,我会从概念定义、执行机制到经典例题,帮你彻底理清。

一、先明确:宏任务(Macrotask)vs 微任务(Microtask)

1. 宏任务(Macrotask)

宏任务是浏览器 / Node.js 执行的大型异步任务单元,每次执行一个宏任务,执行完成后会先清空所有微任务,再进入下一个宏任务。

常见宏任务类型:

  • setTimeoutsetInterval(定时器异步任务)
  • setImmediate(Node.js 环境)
  • requestAnimationFrame(浏览器环境)
  • 脚本整体代码(<script>标签中的同步代码,属于第一个宏任务)
  • I/O 操作(文件读取、网络请求等异步操作)

2. 微任务(Microtask)

微任务是优先级更高的小型异步任务,会在当前宏任务执行完毕后、下一个宏任务执行前,被批量清空(按顺序执行所有微任务)。

常见微任务类型:

  • Promise.then()/Promise.catch()/Promise.finally()(Promise 状态改变后的回调)
  • async/awaitawait后面的代码(本质是 Promise.then 的语法糖,属于微任务)
  • process.nextTick(Node.js 环境,优先级高于普通微任务)
  • MutationObserver(浏览器环境,监听 DOM 变化的回调)

二、核心执行机制(事件循环 Event Loop 关键规则)

  1. 先执行当前宏任务中的同步代码,按代码书写顺序依次执行;
  2. 同步代码执行过程中,遇到异步任务会分别归类到「宏任务队列」或「微任务队列」;
  3. 当当前宏任务的同步代码执行完毕后,立即清空微任务队列:按微任务添加顺序,依次执行所有微任务(若微任务执行过程中产生新的微任务,仍会加入当前微任务队列,一并清空);
  4. 微任务队列清空后,执行一次 UI 渲染(浏览器环境)/ 收尾操作(Node.js 环境);
  5. 从宏任务队列中取出下一个优先级最高的宏任务执行,重复步骤 1-4,形成事件循环。

一句话总结:宏任务按队列排队执行,每个宏任务执行后,必先清空微任务队列,再执行下一个宏任务(微任务优先级 > 宏任务)

三、关键知识点:async/await 的执行逻辑

async/await是 Promise 的语法糖,其执行顺序需重点掌握:

  1. async修饰的函数,会返回一个 Promise 实例,函数内部同步代码正常执行;
  2. await关键字后面的表达式,会先同步执行(获取表达式结果);
  3. await会暂停当前函数的执行,将await后面的代码(即 “后续逻辑”)加入微任务队列(而非同步执行);
  4. await后面的 Promise 状态变为fulfilled后,微任务队列中的 “后续逻辑” 才会执行。

示例验证:

async function testAsync() { console.log("1. await 前的同步代码"); await Promise.resolve("await 结果"); // await 后面的表达式同步执行 console.log("2. await 后的代码(微任务)"); } console.log("0. 全局同步代码"); testAsync(); console.log("3. 全局同步代码后续"); // 执行结果顺序:0 → 1 → 3 → 2 // 解析: // 1. 先执行全局同步代码:输出 0; // 2. 调用 testAsync,执行内部同步代码:输出 1; // 3. 执行 await 后面的 Promise.resolve(同步),然后将 console.log("2...") 加入微任务队列; // 4. 退出 testAsync,继续执行全局同步代码:输出 3; // 5. 当前宏任务(全局脚本)同步代码执行完毕,清空微任务队列:输出 2。

四、经典面试题解析(覆盖 setTimeout/Promise/async/await)

面试题 1:基础版(setTimeout + Promise.then)

console.log("1. 全局同步代码"); setTimeout(() => { console.log("4. setTimeout 宏任务"); }, 0); Promise.resolve() .then(() => { console.log("3. Promise.then 微任务"); }); console.log("2. 全局同步代码后续");
执行结果顺序
1. 全局同步代码 2. 全局同步代码后续 3. Promise.then 微任务 4. setTimeout 宏任务
解析
  1. 执行当前宏任务(全局脚本)的同步代码:先输出 1,再输出 2;
  2. 同步执行过程中:
    • setTimeout回调被加入「宏任务队列」;
    • Promise.resolve()立即变为 fulfilled 状态,then回调被加入「微任务队列」;
  3. 全局同步代码执行完毕,清空微任务队列:输出 3;
  4. 微任务队列清空,取出宏任务队列中的setTimeout回调执行:输出 4。

面试题 2:进阶版(setTimeout + Promise + async/await)

console.log("1. 全局同步代码"); setTimeout(() => { console.log("6. setTimeout 宏任务1"); Promise.resolve().then(() => { console.log("7. setTimeout 内部的微任务"); }); }, 0); setTimeout(() => { console.log("8. setTimeout 宏任务2"); }, 0); async function asyncFunc() { console.log("2. async 内部同步代码"); await Promise.resolve(); console.log("5. await 后的微任务"); } asyncFunc(); Promise.resolve() .then(() => { console.log("4. 全局 Promise 微任务"); }); console.log("3. 全局同步代码后续");
执行结果顺序
1. 全局同步代码 2. async 内部同步代码 3. 全局同步代码后续 4. 全局 Promise 微任务 5. await 后的微任务 6. setTimeout 宏任务1 7. setTimeout 内部的微任务 8. setTimeout 宏任务2
解析
  1. 执行全局宏任务的同步代码:
    • 输出 1;
    • 调用asyncFunc,执行内部同步代码,输出 2;
    • await Promise.resolve()同步执行后,将console.log("5...")加入微任务队列;
    • Promise.resolve().then(...)console.log("4...")加入微任务队列;
    • 输出 3;
  2. 全局同步代码执行完毕,清空微任务队列(按添加顺序执行):
    • 先执行全局 Promise 的微任务,输出 4;
    • 再执行 async/await 的微任务,输出 5;
  3. 微任务队列清空,执行下一个宏任务(第一个 setTimeout):
    • 输出 6;
    • 其内部的Promise.then回调加入微任务队列,立即清空该微任务队列,输出 7;
  4. 第一个 setTimeout 宏任务执行完毕,执行下一个宏任务(第二个 setTimeout):输出 8。

面试题 3:高阶版(含微任务嵌套 + 宏任务优先级)

console.log("1. 全局同步代码"); async function async1() { await async2(); console.log("6. async1 await 后代码"); } async function async2() { console.log("2. async2 同步代码"); return Promise.resolve().then(() => { console.log("4. async2 返回的 Promise 微任务"); }); } async1(); Promise.resolve() .then(() => { console.log("5. 全局 Promise 微任务"); }); setTimeout(() => { console.log("7. setTimeout 宏任务"); Promise.resolve().then(() => { console.log("8. setTimeout 内部微任务"); }); }, 0); console.log("3. 全局同步代码后续");
执行结果顺序
1. 全局同步代码 2. async2 同步代码 3. 全局同步代码后续 4. async2 返回的 Promise 微任务 5. 全局 Promise 微任务 6. async1 await 后代码 7. setTimeout 宏任务 8. setTimeout 内部微任务
解析
  1. 执行全局同步代码:输出 1 → 调用async1→ 执行await async2(),先调用async2
  2. 执行async2同步代码:输出 2 →return Promise.resolve().then(...),该then回调加入微任务队列;
  3. async2执行完毕,await等待其返回的 Promise 完成,将console.log("6...")加入微任务队列(需等async2的 Promise 微任务执行完才会触发);
  4. 继续执行全局同步代码:输出 3 → 全局Promise.then回调加入微任务队列;
  5. 全局同步代码执行完毕,清空微任务队列:
    • 先执行async2内部的 Promise 微任务:输出 4;
    • 再执行全局 Promise 微任务:输出 5;
    • 最后执行async1await 后的微任务:输出 6;
  6. 微任务队列清空,执行setTimeout宏任务:输出 7 → 其内部 Promise 微任务立即执行,输出 8。

五、总结(核心考点提炼)

  1. 队列优先级:微任务队列 > 宏任务队列(当前宏任务执行后,必先清空微任务);
  2. 微任务包含Promise.then/catch/finallyasync/await后续代码;
  3. 宏任务包含setTimeout/setInterval、全局脚本、I/O 操作;
  4. async/await 关键await后面的表达式同步执行,后续代码进入微任务队列;
  5. 执行流程口诀:同步代码先执行 → 微任务队列全清空 → 宏任务队列取一个 → 重复循环。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 11:57:27

震惊!云服务器代理商性价比排行,这3家让你省下千万预算!

震惊&#xff01;云服务器代理商性价比排行&#xff0c;这3家让你省下千万预算&#xff01;在数字化转型浪潮席卷各行各业的今天&#xff0c;云服务器已成为企业IT架构的基石。然而&#xff0c;面对市场上琳琅满目的云服务商和代理商&#xff0c;如何选择一家兼具高性能、高稳定…

作者头像 李华
网站建设 2026/6/14 15:51:40

搞跨端渲染?你绕不开的HarfBuzz原理

文是HarfBuzz系列的第二篇&#xff1a;在这里插入图片描述本文概述在这里插入图片描述一、关键概念与结构1.1 scriptHarfBuzz 中 script 指的是文字系统的类型&#xff0c;注意不是指语言&#xff0c;不同语言也可能属于同一类书写系统&#xff0c;比如&#xff1a;hb_script 举…

作者头像 李华
网站建设 2026/6/15 12:00:28

DX12-1-DirectX3D初始化

什么是 Direct3D 12?DirectX 12 引入了 Direct3D 的下一个版本&#xff0c;即 DirectX 的核心 3D 图形 API。此版本的 Direct3D 比任何以前的版本更快、更高效。Direct3D 12 可实现更丰富的场景、更多的对象、更复杂的效果&#xff0c;以及充分利用现代 GPU 硬件。若要为 Wind…

作者头像 李华
网站建设 2026/6/15 12:03:05

RFSOC学习记录(六)混频模式分析

混频混频器&#xff08;Mixer&#xff09;是RFSOC通过ip核实现在数字域的频率搬移&#xff0c;主要功能是在不改变采样率的情况下&#xff0c;把信号的频谱中心移动到目标频率附近他主要通过一个数控振荡器&#xff0c;生成复指数信号与本振信号相乘&#xff0c;在ip核设置的过…

作者头像 李华
网站建设 2026/6/15 12:01:12

大模型应用开发面经汇总+学习资料包,助你轻松拿下30K+AI岗位!

本文作者分享了近半年面试阿里、腾讯等20多家大模型相关公司的经验&#xff0c;指出大模型应用面试更注重实践而非高深理论&#xff0c;RAG、微调和Agent是重点考察内容。掌握微调原理并实际操作是提升面试通过率的关键。作者还提供了面试准备建议和不同公司的岗位特点&#xf…

作者头像 李华