当前位置: 首页 > news >正文

setInterval问题以及前端如何实现精确的倒计时

一、为什么setInterval不能实现

原因有两:1、js是单线程,基于事件循环执行其他任务(这里建议读者可以多去了解一下浏览器线程与事件循环相关知识)

                  2、setinterval是每隔delay时间,把逻辑放到任务队列中,而不是执行栈中,且如果setinterval中的回调函数执行较长时间,如有for循环等逻辑,那么随着每一次执行误差会越来越大,(会因执行延迟导致误差累积)

二、如何解决

1、webworker:适合长时间定时

原理:Worker独立于主线程运行,不受DOM渲染/事件处理等任务阻塞

思路:主线程向worker线程发送倒计时开始时间戳和总时长(ms)

           worker线程预先计算结束时间戳,通过结束时间戳 - 当前时间戳计算剩余时间,避免传统递减法的累积误差,通过postMessage向主线程发送(基于绝对时间计算剩余时间,而不是简单递减)

            主线程监听并显示UI

self.onmessage = function(e) {
  // 1. 接收主线程传递的初始参数
  const { startTime, duration } = e.data;  // 开始时间戳和总时长(ms)
  const endTime = startTime + duration;    // 预先计算结束时间戳

  function update() {
    // 2. 计算精确剩余时间
    const remaining = Math.max(0, endTime - Date.now());
// 这里也可以用performance.now() ws级别

    // 3. 向主线程发送实时数据
    self.postMessage({
      remaining: remaining,               // 精确到毫秒的剩余时间
      seconds: Math.ceil(remaining / 1000) // 向上取整的秒数(UI显示用)
    });

    if (remaining > 0) {
      // 4. 动态计算下次触发时间(误差补偿核心)
      const nextTick = remaining % 1000 || 1000;
      setTimeout(update, nextTick);
    }
  };

  update(); // 首次启动
};

有个缺点:就是主线程接收到应该渲染的事件后,也是需要去等待当前执行栈清空(比如正在运行的JS代码)和到达浏览器渲染周期才去渲染,实际延迟通常小于1帧(<16ms)这个方法还是有些不足,但是比传统的setinterval会好很多

2、requestAnimation:适合短时间定时

思路:与浏览器刷新率同步+performance.now() 微秒级别+requestAnimation去控制UI更新

这里有个注意点requestAnimation是控制UI渲染的,也就是会让倒计时ui16.6ms就会更新一次,避免了上一个方法中因为事件循环导致ui渲染的去查,精确计算剩余时间靠 performance.now()实现

function update() {
  const remaining = endTime - performance.now(); // 计算剩余时间
  
  if (remaining <= 0) {
    show("时间到!");
    return;
  }

  // 更新UI(但不需要每秒更新60次!)
  show(Math.ceil(remaining / 1000) + "秒"); 
  
  requestAnimationFrame(update); // 继续循环
}

requestAnimationFrame(update); // 启动

3、最好的是requestanimation与webworker+performance.now()结合 requestanimation解决ui渲染问题,webworker+performance.now()解决精确计时问题

4、服务器同步时间:适合抢购、秒杀等场景

  1. 同步阶段

    • 获取服务器时间与本地时间的差值 timeDiff

  2. 倒计时阶段

    • 始终通过 Date.now() + timeDiff 获取真实服务器时间

    • 基于服务器时间计算剩余时长

相关文章:

  • Error 1062 (23000): Duplicate entry ‘‘ for key ‘id‘`
  • Perl 发送邮件
  • 24统计建模国奖作品分享+写作框架提取3
  • Cursor 在前端需求开发工作流中的应用|得物技术
  • 根据日期格式化的常见规则和标准
  • 编程助手fitten code使用说明(超详细)(vscode)
  • spring mvc的拦截器HandlerInterceptor 接口详解
  • Java面试黄金宝典42
  • 2.ElasticSearch-Java API
  • 面试算法高频04-分治与回溯
  • Redis 简介+部署+常用命令!!!
  • 探秘传感器类型:解锁科技新视界
  • 关于使用python 安装 flask-openapi3扩展,使用docker 环境的完整复盘
  • oracle 索引失效
  • ffmpeg基础指令学习
  • SQL:DDL(数据定义语言)和DML(数据操作语言)
  • 旅游-第16届蓝桥第3次STEMA测评Scratch真题第2题
  • JavaScript 事件对象(Event)
  • redis集群模式
  • 数据驱动可视化实战:图表狐精准生成图表的完整数据范式
  • 800字以上网站设计方案/上海百度移动关键词排名优化
  • 网站建设全套教程/郴州seo外包
  • wap建站系统/怎么关闭seo综合查询
  • 个人网站建设需要备案吗/合肥网站seo公司
  • 怎么做网站测试/b站推广网站入口2023是什么
  • 网站的基本概念/如何做网络推广赚钱