你可能不知道的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. MIUI 10以上版本通用线刷ROOT方法

    1.高版本系统解锁 (解锁工具下载) http://www.miui.com/unlock/index.html 注意事项:登陆解锁工具的账号必须是登陆小米手机的账号 解锁步骤在解锁工具上有说明,就两 ...

  2. 信号(signal)

    1.信号本质 1)信号是一种软件中断,是在软件层次上对中断的模拟: 2).在日常生活中也有很多信号,比如常见的红绿灯信号,我们看见红灯就停下,linux中的信号也是类似的,它提供一种机制告诉某个进程在 ...

  3. JAVA 8 主要新特性 ----------------(七)新时间日期 API -----LocalDate

    一.改版原因 1.老板的Date和Calander存在问题,日期操作名称混乱,有的在text下,有的在util下,包名混乱         2.Simple包混乱,致命错误线程不安全.        ...

  4. 学以致用二十九-----python3连接mysql

    在前面安装好mysql后,在一个项目中需要连接mysql,python是3.6版本 python3连接mysql需要安装pymysql模块 可以通过pip安装 查看pip 版本 pip --versi ...

  5. ORACLE 快速刷新物化视图的方法(11g)

    1.on demand:用户需要刷新的时候刷新,这里就要求用户自己动手去刷新数据了(也可以使用job定时刷新) refresh [fast|complete|force] 视图刷新的方式: compl ...

  6. EmEditor注册码

    Emeditor 是一款非常好用的文本编辑器 Emeditor 注册码 姓 名:ttrar.com 序 列 号:DKAZQ-R9TYP-5SM2A-9Z8KD-3E2RK 姓 名:whyida 序 列 ...

  7. 可遇不可求的Question之MySqlClient的Guid 类型的映射篇

    关于 Guid 类型的映射 MySql 没有原生的 Guid 类型,一般使用 binary(16) 或者 char(36) 这两个类型.早期版本的 Connector/Net 将 binary(16) ...

  8. 团队-Forward团队-团队一阶段互评

    团队名称:Forward团队 学号:2015035107105 得分:7 原因:知识欠缺,能够一边学一边做 学号:2015035107109 得分:6 原因:对我有很多帮助 学号:2015035107 ...

  9. 安装virtualbox出现2503、2502的错误提示解决方法

    安装virtualbox右键选择以管理员的身份打开即可

  10. 如何在Linux(Ubuntu)上安装Redmine

    费话就不多说了,本文主要描述如何在如何在Linux(Ubuntu)上安装Redmine.通过这篇文章你将了解如下内容. TL;DR 在Linux(Ubuntu)上安装Redmine的基本流程 配置ph ...