前端面试宝典

事件循环面试题(含 script、UI渲染、nextTick)

这是一道前端高频经典面试题,完美覆盖你要的:同步script、微任务(nextTick)、宏任务、UI渲染、事件循环执行顺序,难度适中,能精准考察核心知识点。


面试题题干

请写出以下代码的执行输出顺序,并说明理由:

环境:浏览器环境(Vue3 环境,nextTick 为浏览器原生 Promise.then 微任务)

console.log('script start');

// 宏任务:定时器
setTimeout(() => {
  console.log('setTimeout');
}, 0);

// Vue nextTick 微任务
this.$nextTick(() => {
  console.log('nextTick1');
});

// 微任务:Promise
Promise.resolve().then(() => {
  console.log('Promise1');
}).then(() => {
  console.log('Promise2');
});

// 触发UI渲染任务
document.body.style.backgroundColor = 'red';
requestAnimationFrame(() => {
  console.log('UI render 渲染完成');
});

console.log('script end');

核心考察点

  1. 同步script代码 执行优先级
  2. 微任务(nextTick/Promise)和 宏任务(setTimeout)的区分
  3. UI渲染 在事件循环中的执行时机
  4. 微任务队列的清空规则
  5. Vue nextTick 本质(微任务)

标准答案 & 执行顺序

script start
script end
nextTick1
Promise1
Promise2
UI render 渲染完成
setTimeout

详细解析(面试口述版)

我按照浏览器事件循环标准流程分步解释:

第一步:执行 同步script代码(全局同步任务)

浏览器首先执行主线程同步script

  1. 输出:script start
  2. 注册 setTimeout 宏任务,放入宏任务队列
  3. 注册 nextTick 微任务,放入微任务队列
  4. 注册 Promise 微任务,放入微任务队列
  5. 修改DOM样式(仅更新DOM树,不立即渲染UI
  6. 注册 requestAnimationFrame(UI渲染前执行)
  7. 输出:script end

第二步:清空当前所有 微任务队列

同步代码执行完毕,立即清空微任务队列(微任务优先于UI渲染和宏任务):

  1. 执行 nextTick1 → 输出
  2. 执行 Promise1 → 输出
  3. Promise1.then 产生新微任务,继续执行 Promise2 → 输出

关键点:Vue nextTick 就是微任务,和Promise同级执行。


第三步:执行 UI渲染(浏览器重绘/重排)

微任务全部清空后,浏览器才会执行UI渲染

  1. 把红色样式渲染到页面
  2. 执行 requestAnimationFrame → 输出:UI render 渲染完成

关键点:UI渲染在微任务之后、宏任务之前


第四步:执行下一个 宏任务

UI渲染完成,事件循环读取宏任务队列:

  1. 执行 setTimeout → 输出:setTimeout

终极执行规则(面试必背)

浏览器事件循环固定顺序:

  1. 同步script代码
  2. 清空所有微任务(nextTick / Promise)
  3. UI渲染
  4. 执行一个宏任务(setTimeout/setInterval)
  5. 回到步骤2,循环执行

总结

  1. 同步 > 微任务(nextTick/Promise) > UI渲染 > 宏任务(setTimeout)
  2. nextTick 是标准微任务,和Promise按注册顺序执行
  3. 修改DOM不会立即渲染,必须等微任务清空后才渲染
  4. 一次事件循环只执行一个宏任务,但会执行所有微任务