你可能不知道的setInterval的坑

之前印象中一直记得setInterval有一些坑,但是一直不是很清楚那些坑是什么。今天去摸索了下之后,决定来做个记录以免自己忘记,也希望让更多人了解到这个坑。

坑的地方

  1. setInterval会无视代码的错误。就算遇到了错误,它还是会一直循环下去,不会停止。这就导致了可能你代码里存在着一些问题(比如你的代码可能有个一定概率下会发生的错误,而你使用setinterval来循环调用它,由于setinterval不会因为报错停止,所以这个问题可能被隐藏),可是却很难发现。

    let count = 1;
    setInterval(function () {
    count++;
    console.log(count);
    if (count % 3 === 0) throw new Error('setInterval报错');
    }, 1000)
  2. setInterval会无视任何情况下定时执行。而在有些场景下,我们是不希望如此的。

    比如说,我们要实现一个功能,每隔一段时间要向服务器发送请求来查看是否有新数据。此时,若当时用户的网络状态很糟糕,客户端收到请求响应的时间大于interval循环的时间。而setInterval会无视任何情况下定时执行,这就会导致了用户的客户端里充斥着ajax请求。

    此时正确的做法应该是改用setTimeout,当用户发出去的请求得到响应或者超时后,再使用setTimeout递归发送下一个请求。这样就不会有setInterval的坑了。

  3. setInterval不能确保每次调用都能执行。我们可以先看一个代码

    const startDate = new Date();
    let endData;
    // 第一个调用会被略过
    setInterval(() => {
    console.log('start');
    console.log(startDate.getTime());
    console.log(endDate.getTime());
    console.log('end');
    }, 1000);
    while (startDate.getTime() + 2 * 1000 > (new Date()).getTime()) {
    }
    endDate = new Date();

    我们可以看到,第一次执行的setInterval函数输出的startDate和endDate差距在2s以上。而我们的setInterval写的是每间隔1s执行一次。因此,我们可以看出,第一次的setInterval函数调用被略过了。

    这说明了:如果说你的代码执行时间会比较久的话,就会导致setInterval中的一部分函数调用被略过。因此你的程序如果依赖于setInterval的精确执行的话,那么你就要小心这一点了。

    当然,其实setTimeout也有这个问题。浏览器的定时器都不是精确执行的。就算你调用setTimeout(fn, 0),它也不能确保马上执行。

解决方案

其实解决方案也很简单,就是使用setTimeout,然后再setTimeout里递归调用。

比如说第一个和第二个坑就可以这样写:

function fn () {
setTimeout(() => {
// 程序主逻辑代码
// 循环递归调用
fn();
}, 1000);
}
fn();

可是使用setTimeout后,我们又可能会遇到一个问题,就是计时器的下次触发时间是在当前的触发时间上开始计算的。这对于第二个坑这种情况是合理的,可是有时候我们又希望它能“匀速”地被触发。也就是说,希望计时器的触发时间尽可能在计时器注册时间+周期*delay附近。这个时候,我们就可以用预期下次发生的时间减去当前的时间来得到一个精确的delayTime。

我写了一个简单的函数来实现这一点:一开始调用该函数的时候,会记录当前的计时器注册时间,以及一个用来统计计算器调用次数的变量。之后在每次调用newFn的时候,都会使用预期下次发生的时间减去当前的时间来得到一个精确的delayTime。这样至少可以保证在一些情况下,计时器可以稍微精确的执行。

function accurateTimers (fn, expectDelayTime) {
let init = false;
let registDate = new Date(); // 计时器注册时间
let count = 0; // 计时器调用次数
function newFn() {
let delayTime;
count++;
if (!init) {
init = true;
delayTime = expectDelayTime;
} else {
delayTime = expectDelayTime * count + registDate.getTime() - new Date().getTime();
}
console.log(delayTime);
setTimeout(() => {
fn();
newFn();
}, delayTime);
}
newFn();
}
accurateTimers(function () {
let startDate = new Date();
// 延迟500ms
while (startDate.getTime() + 500 > (new Date()).getTime()) {
}
}, 1000);

结论

以上,就是本次文章的内容。这篇文章只是做一个简单的记录,希望能帮大家了解到setInterval的坑的地方,在实际编程中可以少走点弯路。如果觉得有用的话,欢迎点个赞或者关注哦。谢谢。

你可能不知道的setInterval的坑的更多相关文章

  1. 你所不知道的setInterval

    在你所不知道的setTimeout记载了下setTimeout相关,此篇则整理了下setInterval:作为拥有广泛应用场景(定时器,轮播图,动画效果,自动滚动等等),而又充满各种不确定性的这set ...

  2. 你所不知道的setTimeout

    JavaScript提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成.它们向任务队列添加定时任务.初始接触它的人都觉得好简单 ...

  3. 关于RecyclerView你知道的不知道的都在这了(下)

    目录 目录 正文 6. Recycler 7. ItemAnimator 8. ItemDecoration 9. OnFlingListener 目录 由于本篇篇幅特长,特意做了个目录,让大伙对本篇 ...

  4. JavaScript 优雅的实现方式包含你可能不知道的知识点

    有些东西很好用,但是你未必知道:有些东西你可能用过,但是你未必知道原理. 实现一个目的有多种途径,俗话说,条条大路通罗马.很多内容来自平时的一些收集以及过往博客文章底下的精彩评论,收集整理拓展一波,发 ...

  5. 你所不知道的 CSS 阴影技巧与细节 滚动视差?CSS 不在话下 神奇的选择器 :focus-within 当角色转换为面试官之后 NPOI 教程 - 3.2 打印相关设置 前端XSS相关整理 委托入门案例

    你所不知道的 CSS 阴影技巧与细节   关于 CSS 阴影,之前已经有写过一篇,box-shadow 与 filter:drop-shadow 详解及奇技淫巧,介绍了一些关于 box-shadow  ...

  6. 关于setTimeout()你所不知道的地方,详解setTimeout()

    关于setTimeout()你所不知道的地方,详解setTimeout() 前言:看了这篇文章,1.注意setTimeout引用的是全部变量还是局部变量了,当直接调用外部函数方法时,实际上函数内部的变 ...

  7. 你可能不知道的陷阱, IEnumerable接口

    1.  IEnumerable 与  IEnumerator IEnumerable枚举器接口的重要性,说一万句话都不过分.几乎所有集合都实现了这个接口,Linq的核心也依赖于这个万能的接口.C语言的 ...

  8. 你真的会玩SQL吗?你所不知道的 数据聚合

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  9. 你所不知道的linq(二)

    上一篇说了from in select的本质,具体参见你所不知道的linq.本篇说下from...in... from... in... select 首先上一段代码,猜猜结果是什么? class P ...

随机推荐

  1. window下载android 最新源码

    https://blog.csdn.net/jason0539/article/details/24010633 2017方法 ok https://www.cnblogs.com/zhangting ...

  2. go网络编程应用

    网络编程基础(聊天室) 服务端 package main import ( "fmt" "net" "strings" "os&q ...

  3. vue watch高级用法

    watch是vue组件最常用,最重要的功能之一,我们都知道它最基本的特性就是监听的data对象里的属性变化了,watch就会自动执行. 那它还有什么骚操作呢?下面我带大家看看,菜鸟一枚,如有错误欢迎指 ...

  4. 不停止nginx服务,使配置文件生效

    ps -ef | grep "nginx: master process" | grep -v "grep" | awk -F ' ' '{print $2}' ...

  5. MIPS汇编指令集

    MIPS有三种指令格式: R型 6 5 5 5 5 6 op rs rt rd shamt funct 功能:寄存器-寄存器ALU操作 (算术运算,逻辑运算) I型 6 5 5 16 op rs rt ...

  6. FPGA中带优先级的if else if与不带优先级的case的探讨

    我们知道在书本上都说让我们尽量使用不带优先级的的数据选择器,今天我们就来探讨一下二者的区别. 例子1:带优先级的的数据选择器,综合成功,且没有任何警告. module detection_prio # ...

  7. autotrace执行计划中,统计信息详解

    全表扫描是怎么扫描的? oracle最小的存储单位是block 物理上连续的block组成了extent(也就是说一个区中的所有块在物理上是连续的) 很多个extent组成了segment(一个seg ...

  8. Akka-Cluster(5)- load-balancing with backoff-supervised stateless computation - 无状态任务集群节点均衡分配

    分布式程序运算是一种水平扩展(scale-out)运算模式,其核心思想是能够充分利用服务器集群中每个服务器节点的计算资源,包括:CPU.内存.硬盘.IO总线等.首先对计算任务进行分割,然后把细分的任务 ...

  9. 5个相见恨晚的Linux命令

    阅读本文大概需要 2.4 分钟. 作者 | 李火清 转载自[CU技术社区] 编者按:说到Linux命令相信大家都不陌生,就连前端现在也要经常在 terminal 敲一些 node,gulp等命令,本文 ...

  10. 第71节:Java中HTTP和Servlet

    第71节:Java中HTTP和Servlet 前言 哭着也要看完!!!字数: 学习xml和TomCat 会写xml,看懂xml 解析对象 SAXReader reader = new SAXReade ...