setInterval倒计时切换页面后不准

背景

最近在做一个倒计时时,发现当切换浏览器tab后,再切回倒计时页面,倒计时的数据不准,比真正的剩余时间多,短时间还好,时间长了,计时器的误差会很大。

原因

倒计时是用setInterval每1000毫秒触发一次。

在进入页面时,计算剩余时间,把剩余时间用setInterval每1000毫秒触发一次进行减法。

但是由于浏览器的优化机制,为了更极致的优化,在切换tab之后浏览器会把setInterval的执行效率降低,在浏览器窗口非激活的状态下会停止工作或者以极慢的速度工作。那么这时候就不是1000毫秒减一次了,所以会有误差。

用setInterval实现计时

var start = new Date().getTime(), count = 0;
var interval = setInterval(function () {count++console.log(new Date().getTime() - (start + count * 1000) + 'ms')if(count == 10){clearInterval(interval);}
}, 1000)

image.png

可以看到,我打印的new Date().getTime() - (start + count * 1000) ,
也就是每次计时的误差,理想情况下,应该是0。

用setTimeout实现计时

var start = new Date().getTime(), count = 0,interval = 1000;
var timer = setTimeout(doFunc,interval);
function doFunc(){count++console.log(new Date().getTime() - (start + count * 1000) + 'ms');if(count < 10){timer = setTimeout(doFunc,interval);}
}

image.png

也是一样的会出现误差

setInterval、setTimeout误差的不同之处

setInterval指定的是“开始执行”之间的间隔,并不考虑每次任务执行本身所消耗的时间。因此实际上,两次执行之间的间隔会小于指定的时间。比如,setInterval指定每 100ms 执行一次,每次执行需要 5ms,那么第一次执行结束后95毫秒,第二次执行就会开始。如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。

为了确保两次执行之间有固定的间隔,可以不用setInterval,而是每次执行结束后,使用setTimeout指定下一次执行的具体时间。

模拟阻塞事件

setInterval

//阻塞代码
setInterval(function () {var n = 0while (n++ < 1000000000);
}, 1000)var start = new Date().getTime(), count = 0;
var interval = setInterval(function () {count++console.log(new Date().getTime() - (start + count * 1000) + 'ms')if(count == 10){clearInterval(interval);}
}, 1000)

image.png

setTimeout

//阻塞代码
setInterval(function () {var n = 0while (n++ < 1000000000);
}, 1000)var start = new Date().getTime(), count = 0,interval = 1000;
var timer = setTimeout(doFunc,interval);
function doFunc(){count++console.log(new Date().getTime() - (start + count * 1000) + 'ms');if(count < 10){timer = setTimeout(doFunc,interval);}
}

image.png

可以看到加了一些阻塞线程的代码后,误差越来越严重,
在实际项目中,执行计时器的同时,会有很多其他异步阻塞事件,会导致倒计时功能不精确。

解决方案

1、setInterval每次触发的时候,重新计算剩余时间(误差在一分钟以内)

2、Web Workers(这个在nuxt中引入会报错,涉及到webpack改动较大,暂时不用)

3、进行误差修正,也就是获取到误差的值,并且根据这个误差值来动态调整我们执行回调的间隔时间

  • 计算误差值
  • 动态调整执行setTimeout的间隔

加上动态误差修正

var start = new Date().getTime(), count = 0,interval = 1000;
var offset = 0;//误差时间
var nextTime = interval - offset;//原本间隔时间 - 误差时间
var timer = setTimeout(doFunc,nextTime);
function doFunc(){count++console.log(new Date().getTime() - (start + count * interval) + 'ms');offset = new Date().getTime() - (start + count * interval);nextTime = interval - offset;if (nextTime < 0) { nextTime = 0; }if(count < 10){timer = setTimeout(doFunc,nextTime);}
}

试试效果:

image.png

把每次的nextTime打印出来看看:

image.png

以看到每次的nextTime都会根据上次的误差值来动态调整,以尽量减少整体的误差。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/111759.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

欧拉筛和埃氏筛(超详细分析筛选过程,差异,证明,时间比较)

分析之前我们先看一下埃氏筛和欧拉筛的代码&#xff1a; 1.Eraosthenes&#xff08;埃拉托斯尼筛法&#xff09;埃氏筛法 时间复杂度O&#xff08;nlogn&#xff09; const int maxn2e66; bool isprime[maxn]; void seive(){memset(isprime,true,sizeof(isprime));isprime[0…

目标检测中生成锚框函数详解

%matplotlib inline import torch from d2l import torch as d2l torch.set_printoptions(2) # 让pytorch打印张量时&#xff0c;只打印到小数点后两位将设一张图片&#xff0c;宽和高为2,2 X torch.rand(size(1,3,2,2)) Y generate_anchors(X,sizes[0.75,0.5,0.25],ratios[…

webpack配置alias后eslint和ts无法识别

背景 我们在 webpack 配置 alias 后&#xff0c;发现项目中引入的时候&#xff0c;还是会报错&#xff0c;如下&#xff1a; 可以看到&#xff0c;有一个是 ts报错&#xff0c;还有一个是 eslint 报错。 解决 ts 报错 tsconfig.json {"compilerOptions": {...&q…

基于Xml方式的Bean的配置-Bean的作用范围scope配置

SpringBean的配置详解 Bean的配置范围 默认情况下&#xff08;基本的Spring环境&#xff09;&#xff0c;单纯Spring环境Bean的作用范围有两个&#xff1a;Singleton和prototypesingleton:单例&#xff0c;默认值&#xff0c;Spring容器创建的时候&#xff0c;就会进行Bean的实…

TCP详解之流量控制

TCP详解之流量控制 发送方不能无脑的发数据给接收方&#xff0c;要考虑接收方处理能力。 如果一直无脑的发数据给对方&#xff0c;但对方处理不过来&#xff0c;那么就会导致触发重发机制&#xff0c;从而导致网络流量的无端的浪费。 为了解决这种现象发生&#xff0c;TCP 提…

专注写作,快速上线:Cpolar+Inis帮助你在Ubuntu上建立博客网站

文章目录 前言1. Inis博客网站搭建1.1. Inis博客网站下载和安装1.2 Inis博客网站测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总…

LeetCode-热题100-笔记-day31

105. 从前序与中序遍历序列构造二叉树https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c…

使用 Docker 安装 Elasticsearch (本地环境 M1 Mac)

Elasticsearchkibana下载安装 docker pull elasticsearch:7.16.2docker run --name es -d -e ES_JAVA_OPTS“-Xms512m -Xmx512m” -e “discovery.typesingle-node” -p 9200:9200 -p 9300:9300 elasticsearch:7.16.2docker pull kibana:7.16.2docker run --name kibana -e EL…

区域图片上色

目录 下图中&#xff0c;记得点击Apply&#xff0c;然后再点击Symbology 实际选择的时候&#xff0c;不选1Categorized&#xff0c;因为其分段不方便。

现在全国融资融券两融利率最低是多少?哪家证券公司券商费率低?

融资融券是指投资者通过向券商借入资金&#xff08;融资&#xff09;或借入证券&#xff08;融券&#xff09;&#xff0c;以达到获得更高收益、降低交易风险、提高资金利用效率的目的。通过融资&#xff0c;投资者可以用借入的资金买入更多的证券&#xff1b;通过融券&#xf…

C#,数值计算——Hashtable的计算方法与源程序

1 文本格式 using System; using System.Collections; using System.Collections.Generic; namespace Legalsoft.Truffer { public abstract class Hashtable<K> { private int nhash { get; set; } private int nmax { get; set; } pr…

使用ROS与Movelt实现myCobot 280运动轨迹规划和控制

ROS的技术案例 Introduction 今天这篇文章将记录我使用myCobot 280 M5stack 在ROS当中是如何使用的。为什么使用ROS呢&#xff0c;因为提及到机器人都离不开ROS这个操作系统&#xff0c;今天是我们第一次使用ROS这个系统。 今天我将从ROS的介绍&#xff0c;环境的配置以及mycob…