前言

前端项目里使用延时器是正常的,模仿一下异步操作,但是在复杂的业务场景下不建议使用,主要有两点,第一js是单线程,第二是官方提供的api足以解决各种执行队列。

你设置的延时器为1,不一定是在1ms后执行

1. 现在各大浏览器的引擎不足以支撑你的需求:

Chrome/Edge:4ms

Firefox:4ms

Safari:4-5ms

2. js是一个单线程的操作,是有事件循环的

console.log('开始');
setTimeout(() => console.log('延时器'), 1);
console.log('结束');
// 输出顺序:开始 → 结束 → 延时器

即使延时器到期,也要等待当前执行栈清空:

// 即使延时器设置为0,也要等循环执行完
setTimeout(() => console.log('延时器'), 1);
for(let i = 0; i < 10000; i++) {
    console.log(i)
}

nexttick也是用延时器,为啥不一样呢?

Vue 中的 nextTick 底层确实可能用到计时器(如 setTimeout 或 setImmediate),但它并非简单依赖 setTimeout(fn, 0),而是经过了多层优化的 “微任务优先” 方案,因此能避免 setTimeout(0) 的大部分缺点,保证可靠性。

一、nextTick 的底层实现:并非单纯依赖计时器

Vue 的 nextTick 核心目的是等待 DOM 更新完成后执行回调(因为 Vue 的 DOM 更新是异步的)。其实现逻辑是 “优先使用微任务,降级使用宏任务”,具体步骤如下:

  1. 优先尝试微任务:微任务的执行时机在 “当前同步代码执行完毕后、DOM 渲染前”,且延迟远小于宏任务(通常 < 1ms),最适合处理 DOM 更新后的回调。Vue 会按优先级尝试以下微任务 API:

    • Promise.then(浏览器普遍支持,优先级最高);
    • MutationObserver(监听 DOM 变化的 API,本质是微任务)。
  2. 微任务不支持时,降级使用宏任务:若环境不支持微任务(如 IE 浏览器),则会使用宏任务,顺序为:

    • setImmediate(仅 IE 和 Node.js 支持,延迟比 setTimeout 更稳定);
    • 最后才会使用 setTimeout(fn, 0)(兼容性最好,但延迟最高)。

nextTick 虽然底层可能用到 setTimeout,但它是针对 Vue 异步 DOM 更新机制设计的专用方案:通过 “微任务优先” 保证低延迟,通过 “任务合并” 减少性能损耗,通过 “绑定 DOM 更新队列” 保证时机精准。这些优化让它完全避开了 setTimeout(0) 的缺点,成为 Vue 中处理 DOM 异步更新的可靠方案。

简单说:nextTick 是 “经过特殊设计的计时器用法”,而 setTimeout(0) 是 “通用但粗糙的异步化工具”,二者不可同日而语。

页面的刷新频率虽然会造成影响吗?,

有些伙伴觉得,页面的刷新频率导致不能正确的展示出延时器的输出结果,是刷新频率延误了输出时长。实际上这块跟浏览器的渲染关系不是很大

那它是怎么影响的呢?

1. 重绘与回流:页面渲染阻塞 JavaScript 执行

  • 重绘与回流‌:回流是计算页面布局的几何变化,重绘是将像素画到屏幕上。它们共同构成了“渲染”步骤。
  • 阻塞效应‌:当浏览器进入渲染阶段(包括样式计算、布局、绘制),主线程被占用,此时即使延时器的回调已经到了执行时间,也必须‌等待整个渲染步骤完成‌。这就是最直接的“渲染阻塞 JavaScript 执行”。
  • 举个例子‌:假设一个复杂的动画或大量的 DOM 操作触发了回流,这个渲染过程可能耗时 5-10ms。在这期间,你的 1ms 延时器回调只能干等着。

2. 渲染时机:浏览器在下一次重绘前执行积压的任务

这涉及到 setTimeout 与 requestAnimationFrame 的区别。

  • ‌**setTimeout‌:它只关心时间,不关心屏幕刷新。它可能在‌两次屏幕刷新的中间时刻‌执行。如果它的执行过程中修改了样式,浏览器不得不紧急进行样式计算和布局,可能导致当前帧无法完成,或者直接将改动推迟到下一帧去渲染,造成‌丢帧**‌现象。
  • ‌**requestAnimationFrame‌:它的回调函数被设计在‌每一次浏览器渲染之前、样式计算之后**‌执行。这是更新动画、修改样式的最佳时机,能确保修改的样式在紧接着的渲染中被绘制出来,从而保证流畅性。

因此,事件循环中积压的 setTimeout 回调,通常会被安排在当前帧的渲染开始之前执行。如果这些回调执行时间过长,挤占了原本属于渲染的时间,就会导致渲染延迟,用户会觉得页面“卡顿”。

3. 页面加载:如果页面还在加载或渲染,延时器会进一步延迟

  • 解析阻塞‌:浏览器在加载页面时,需要解析 HTML、构建 DOM 树、加载并执行 CSS 和 JavaScript。特别是遇到 <script> 标签(没有 async 或 defer 属性)时,它会停止 HTML 解析,立即下载并执行该脚本。这个过程会严重阻塞事件循环。
  • 举个例子‌:你的 setTimeout 代码可能在页面加载初期就被执行了。但此时,主线程正忙于解析一个巨大的 HTML 文档或执行一个庞大的初始化脚本。你的延时器回调必须等所有这些“正事”干完后,才有机会执行。
  • 初始渲染‌:浏览器会尽快进行首次渲染(例如,渲染出部分文字和背景)。这个首次布局和绘制的开销很大,会进一步推迟你那个“微不足道”的 1ms 延时器。
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]