真实面经题目 · 原创解析
事件循环和渲染的关系是怎么样的?
这题考察浏览器主线程调度:宏任务执行后清空微任务,随后浏览器在合适时机进行渲染;长任务和微任务堆积都会推迟渲染。
真实面经题目 · 原创解析
这题考察浏览器主线程调度:宏任务执行后清空微任务,随后浏览器在合适时机进行渲染;长任务和微任务堆积都会推迟渲染。
浏览器的 JS 执行和渲染大多共享主线程。一轮事件循环通常是取一个 task 执行,例如 script、setTimeout、点击回调;task 结束后清空 microtask 队列,例如 Promise.then、queueMicrotask;之后浏览器如果需要且时机合适,会执行 requestAnimationFrame 回调,并进行样式计算、布局、绘制和合成。渲染不是每轮循环必然发生,但只有主线程空出来才有机会渲染。长 JS 任务会阻塞用户输入和绘制,大量递归微任务也会让页面迟迟没有渲染机会。优化上要拆分长任务、减少同步布局读写、把重计算放到 Worker,并用 requestAnimationFrame 安排视觉更新。
JS 执行、样式计算、布局和绘制很多阶段都依赖主线程。只要 JS 长时间占用主线程,浏览器就无法及时处理输入和更新画面。
浏览器先执行一个宏任务,宏任务结束后清空所有微任务。微任务清空后,浏览器才有机会进入渲染步骤。这个顺序解释了 Promise.then 通常早于 setTimeout 后续任务,也解释了微任务过多会饿住渲染。
浏览器会结合刷新率、页面可见性、是否有样式变化等因素决定是否渲染。没有视觉变化或时机未到时,可能跳过渲染;但主线程被占满时,必要渲染也会被推迟。
rAF 回调会在下一次绘制前执行,适合读取上一帧状态并提交本帧视觉更新。setTimeout 不和帧严格对齐,做动画可能抖动。
一个耗时 200ms 的同步循环会直接阻塞多帧;不断在 Promise.then 里继续 queueMicrotask 也会让微任务队列长期不空,渲染机会被延后。
把大任务切片到多个 task,视觉更新放 rAF,低优先级工作放 requestIdleCallback,CPU 密集计算放 Worker,并避免在同一帧里反复读写布局造成强制同步布局。
通常一个宏任务结束后会先清空微任务队列,然后浏览器才有机会渲染。因此大量微任务可能推迟渲染。
它在浏览器下一次绘制前回调,和刷新节奏对齐,适合集中做本帧视觉更新,减少 setTimeout 动画的抖动。
它会进入任务队列,必须等当前任务、当前任务产生的微任务,以及前面排队的任务处理后才轮到它。
减少一次性同步工作,分片渲染或虚拟列表;把计算移到 Worker;DOM 读写分离,并把视觉提交放到 rAF。