事件循环面试题(含 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');
核心考察点
- 同步script代码 执行优先级
- 微任务(nextTick/Promise)和 宏任务(setTimeout)的区分
- UI渲染 在事件循环中的执行时机
- 微任务队列的清空规则
- Vue
nextTick本质(微任务)
标准答案 & 执行顺序
script start
script end
nextTick1
Promise1
Promise2
UI render 渲染完成
setTimeout
详细解析(面试口述版)
我按照浏览器事件循环标准流程分步解释:
第一步:执行 同步script代码(全局同步任务)
浏览器首先执行主线程同步script:
- 输出:
script start - 注册
setTimeout宏任务,放入宏任务队列 - 注册
nextTick微任务,放入微任务队列 - 注册
Promise微任务,放入微任务队列 - 修改DOM样式(仅更新DOM树,不立即渲染UI)
- 注册
requestAnimationFrame(UI渲染前执行) - 输出:
script end
第二步:清空当前所有 微任务队列
同步代码执行完毕,立即清空微任务队列(微任务优先于UI渲染和宏任务):
- 执行
nextTick1→ 输出 - 执行
Promise1→ 输出 Promise1.then产生新微任务,继续执行Promise2→ 输出
关键点:Vue nextTick 就是微任务,和Promise同级执行。
第三步:执行 UI渲染(浏览器重绘/重排)
微任务全部清空后,浏览器才会执行UI渲染:
- 把红色样式渲染到页面
- 执行
requestAnimationFrame→ 输出:UI render 渲染完成
关键点:UI渲染在微任务之后、宏任务之前。
第四步:执行下一个 宏任务
UI渲染完成,事件循环读取宏任务队列:
- 执行
setTimeout→ 输出:setTimeout
终极执行规则(面试必背)
浏览器事件循环固定顺序:
- 同步script代码
- 清空所有微任务(nextTick / Promise)
- UI渲染
- 执行一个宏任务(setTimeout/setInterval)
- 回到步骤2,循环执行
总结
- 同步 > 微任务(nextTick/Promise) > UI渲染 > 宏任务(setTimeout)
nextTick是标准微任务,和Promise按注册顺序执行- 修改DOM不会立即渲染,必须等微任务清空后才渲染
- 一次事件循环只执行一个宏任务,但会执行所有微任务