为什么不建议项目里用延时器作为规定时间内的业务操作

前言

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

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

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

Chrome/Edge:4ms

Firefox:4ms

Safari:4-5ms

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

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

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

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

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

Vue 中的 nextTick 底层确实可能用到计时器(如 setTimeoutsetImmediate),但它并非简单依赖 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. 渲染时机:浏览器在下一次重绘前执行积压的任务

这涉及到 setTimeoutrequestAnimationFrame 的区别。

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

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

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

  • 解析阻塞 ‌:浏览器在加载页面时,需要解析 HTML、构建 DOM 树、加载并执行 CSS 和 JavaScript。特别是遇到 <script> 标签(没有 asyncdefer 属性)时,它会停止 HTML 解析,立即下载并执行该脚本。这个过程会严重阻塞事件循环。
  • 举个例子 ‌:你的 setTimeout 代码可能在页面加载初期就被执行了。但此时,主线程正忙于解析一个巨大的 HTML 文档或执行一个庞大的初始化脚本。你的延时器回调必须等所有这些"正事"干完后,才有机会执行。
  • 初始渲染‌:浏览器会尽快进行首次渲染(例如,渲染出部分文字和背景)。这个首次布局和绘制的开销很大,会进一步推迟你那个"微不足道"的 1ms 延时器。
相关推荐
该用户已不存在5 小时前
Gemini CLI 扩展,把Nano Banana 搬到终端
前端·后端·ai编程
地方地方5 小时前
前端踩坑记:解决图片与 Div 换行间隙的隐藏元凶
前端·javascript
炒米23335 小时前
【Array】数组的方法
javascript
jason_yang5 小时前
vue3+element-plus按需自动导入-正确姿势
vue.js·vite·element
小猫由里香5 小时前
小程序打开文件(文件流、地址链接)封装
前端
Tzarevich5 小时前
使用n8n工作流自动化生成每日科技新闻速览:告别信息过载,拥抱智能阅读
前端
voidmuse5 小时前
带你零基础实现一个cursor (1):从零开始实现深度搜索功能-Function Call的介绍
javascript
掘金一周5 小时前
一个前端工程师的年度作品:从零开发媲美商业级应用的后台管理系统 | 掘金一周 10.23
前端·人工智能·后端
大杯咖啡5 小时前
前端常见的6种设计模式
前端·javascript