无限极服务
146.43MB · 2025-10-24
前端项目里使用延时器是正常的,模仿一下异步操作,但是在复杂的业务场景下不建议使用,主要有两点,第一js是单线程,第二是官方提供的api足以解决各种执行队列。
Chrome/Edge:4ms
Firefox:4ms
Safari:4-5ms
console.log('开始');
setTimeout(() => console.log('延时器'), 1);
console.log('结束');
// 输出顺序:开始 → 结束 → 延时器
即使延时器到期,也要等待当前执行栈清空:
// 即使延时器设置为0,也要等循环执行完
setTimeout(() => console.log('延时器'), 1);
for(let i = 0; i < 10000; i++) {
console.log(i)
}
Vue 中的 nextTick 底层确实可能用到计时器(如 setTimeout 或 setImmediate),但它并非简单依赖 setTimeout(fn, 0),而是经过了多层优化的 “微任务优先” 方案,因此能避免 setTimeout(0) 的大部分缺点,保证可靠性。
nextTick 的底层实现:并非单纯依赖计时器Vue 的 nextTick 核心目的是等待 DOM 更新完成后执行回调(因为 Vue 的 DOM 更新是异步的)。其实现逻辑是 “优先使用微任务,降级使用宏任务”,具体步骤如下:
优先尝试微任务:微任务的执行时机在 “当前同步代码执行完毕后、DOM 渲染前”,且延迟远小于宏任务(通常 < 1ms),最适合处理 DOM 更新后的回调。Vue 会按优先级尝试以下微任务 API:
Promise.then(浏览器普遍支持,优先级最高);MutationObserver(监听 DOM 变化的 API,本质是微任务)。微任务不支持时,降级使用宏任务:若环境不支持微任务(如 IE 浏览器),则会使用宏任务,顺序为:
setImmediate(仅 IE 和 Node.js 支持,延迟比 setTimeout 更稳定);setTimeout(fn, 0)(兼容性最好,但延迟最高)。nextTick 虽然底层可能用到 setTimeout,但它是针对 Vue 异步 DOM 更新机制设计的专用方案:通过 “微任务优先” 保证低延迟,通过 “任务合并” 减少性能损耗,通过 “绑定 DOM 更新队列” 保证时机精准。这些优化让它完全避开了 setTimeout(0) 的缺点,成为 Vue 中处理 DOM 异步更新的可靠方案。
简单说:nextTick 是 “经过特殊设计的计时器用法”,而 setTimeout(0) 是 “通用但粗糙的异步化工具”,二者不可同日而语。
有些伙伴觉得,页面的刷新频率导致不能正确的展示出延时器的输出结果,是刷新频率延误了输出时长。实际上这块跟浏览器的渲染关系不是很大
这涉及到 setTimeout 与 requestAnimationFrame 的区别。
setTimeout:它只关心时间,不关心屏幕刷新。它可能在两次屏幕刷新的中间时刻执行。如果它的执行过程中修改了样式,浏览器不得不紧急进行样式计算和布局,可能导致当前帧无法完成,或者直接将改动推迟到下一帧去渲染,造成丢帧**现象。requestAnimationFrame:它的回调函数被设计在每一次浏览器渲染之前、样式计算之后**执行。这是更新动画、修改样式的最佳时机,能确保修改的样式在紧接着的渲染中被绘制出来,从而保证流畅性。因此,事件循环中积压的 setTimeout 回调,通常会被安排在当前帧的渲染开始之前执行。如果这些回调执行时间过长,挤占了原本属于渲染的时间,就会导致渲染延迟,用户会觉得页面“卡顿”。
<script> 标签(没有 async 或 defer 属性)时,它会停止 HTML 解析,立即下载并执行该脚本。这个过程会严重阻塞事件循环。setTimeout 代码可能在页面加载初期就被执行了。但此时,主线程正忙于解析一个巨大的 HTML 文档或执行一个庞大的初始化脚本。你的延时器回调必须等所有这些“正事”干完后,才有机会执行。