面试官:讲一下输出顺序🙃

一位学长面试的时候遇到的题目

JavaScript 的事件循环(event loop)是一种机制,它负责执行异步代码并管理调用栈任务队列 之间的交互,确保异步操作如定时器、 promises 和 I/O 操作能够在适当的时候被执行。简而言之,事件循环使得 JavaScript 能够处理并发操作,而无需多线程。

一、核心机制

  • 同步优先原则:主线程同步代码立即执行
  • 微任务优先原则:所有微任务(Promise、queueMicrotask)会在当前宏任务结束后立即执行
  • 渲染时机:每完成一个宏任务后可能触发页面渲染
  • 任务队列管理:宏任务队列(setTimeout等)每次只取一个任务执行

二、异步任务类型

宏任务(MacroTask)

类型 触发场景 执行优先级 所属环境 关键特性
script 整体代码 页面加载/脚本执行 最高 浏览器 首个默认执行的宏任务
setTimeout 定时器到期 通用 最小延迟4ms(浏览器规范)
setInterval 周期性定时器 通用 可能堆积任务
I/O 操作 文件读写、网络请求(fetch/XHR) 通用 回调进入任务队列
DOM 事件 click/keydown 等交互事件 浏览器 事件触发时入队
requestAnimationFrame 动画帧回调 中高 浏览器 在渲染前执行
MessageChannel 跨文档通信 浏览器 优先级高于 setTimeout
setImmediate Node.js 立即执行 Node.js 比 setTimeout(0) 更快
  • 宏任务执行特点: 每个宏任务执行后都会重新检查微任务队列
javascript 复制代码
// 浏览器环境示例
setTimeout(() => console.log('Timeout1'), 0);
setTimeout(() => {
  console.log('Timeout2');
  setTimeout(() => console.log('Nested Timeout'), 0);
}, 0);

// 输出顺序:Timeout1 → Timeout2 → Nested Timeout

微任务(MicroTask)

类型 触发机制 执行优先级 所属环境 关键特性
Promise.then Promise 状态变更(resolve/reject) 通用 链式调用形成微任务队列
queueMicrotask 直接调用 通用 官方推荐的微任务API
MutationObserver DOM 变更观测 浏览器 替代已废弃的 Mutation Events
process.nextTick Node.js 阶段切换 极高 Node.js 优先于所有微任务
  • 微任务执行特点: 微任务队列必须完全清空才会执行下一个宏任务
javascript 复制代码
// 微任务嵌套示例
Promise.resolve().then(() => {
  console.log('Microtask1');
  queueMicrotask(() => console.log('Nested Microtask'));
});

Promise.resolve().then(() => console.log('Microtask2'));

/* 执行顺序:
Microtask1 → Microtask2 → Nested Microtask
 */

三、原理实践

javascript 复制代码
console.log('同步任务开始');

setTimeout(() => {
  console.log('宏任务1启动');
  Promise.resolve().then(() => {
    console.log('宏任务1的微任务');
    queueMicrotask(() => console.log('嵌套微任务'));
  });
});

setTimeout(() => console.log('宏任务2'));

Promise.resolve().then(() => console.log('初始微任务'));

console.log('同步任务结束');

/* 执行结果:
同步任务开始
同步任务结束
初始微任务        ← 第一个宏任务(script)后的微任务
宏任务1启动
宏任务1的微任务   ← 第二个宏任务后的第一层微任务
嵌套微任务        ← 第二层嵌套微任务
宏任务2          ← 第三个宏任务 
*/

四、面试题解析

javascript 复制代码
async function async1() {
  console.log('E'); // 1️⃣ 同步执行
  await async2();   // 2️⃣ 触发微任务
  console.log('F'); // 4️⃣ 微任务执行
}

async function async2() {
  console.log('G'); // 3️⃣ 同步执行
}

setTimeout(() => console.log('H'), 0); // 6️⃣ 宏任务

async1(); // 触发执行流

new Promise((res) => {
  console.log('I'); // 5️⃣ 同步执行
  res();
}).then(() => console.log('J')); // 5️⃣ 微任务

执行顺序详解:

  1. 同步阶段:

    • 执行 async1() 输出 E
    • 执行 async2() 输出 G
    • 执行 Promise 构造函数输出 I
  2. 微任务队列:

    • await async2() 后的代码 → 输出 F注意
    • Promise.then → 输出 J
  3. 宏任务队列:

    • setTimeout 回调 → 输出 H

关键机制解析:

javascript 复制代码
// async/await 本质是 Promise 语法糖
async function example() {
  await somePromise;
  console.log('后续代码');
}

// 等价于
function example() {
  return somePromise.then(() => {
    console.log('后续代码');
  });
}

五、总结

希望下次我遇到这种题目不会陷入死一样的沉默😅

相关推荐
Re.不晚4 分钟前
Web前端开发——HTML基础下
前端·javascript·html
vjmap8 分钟前
如何免费使用AI编程工具(如Trae或Cursor)生成CAD图纸?
javascript
浪遏16 分钟前
面试官😏: 讲一下事件循环 ,顺便做道题🤪
前端·面试
uhakadotcom19 分钟前
Pandas入门:数据处理和分析的强大工具
后端·面试·github
浪遏1 小时前
面试官:字符串反转有多少种实现方式 ?| 一道题目检测你的基础
前端·面试
码农CV1 小时前
Java基础面试题全集
java·面试
IT、木易1 小时前
大白话CSS 优先级计算规则的详细推导与示例
前端·css·面试
歡進2 小时前
开源 | Warpvas 实现扭曲的画布
javascript·webgl·canvas
开开心心就好2 小时前
电脑睡眠智能管控:定时、依状态灵活调整,多模式随心选
javascript·windows·python·安全·django·pdf·电脑
技术蔡蔡2 小时前
Android多线程开发之线程安全
android·面试