你可能不知道的setinterval的坑(代码片段)

陈陈jg 陈陈jg     2022-11-23     431

关键词:

之前印象中一直记得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。

我写了一个简单的函数来说明这一点:一开始调用该函数的时候,会记录当前的计时器注册时间,以及一个用来统计计算器调用次数的变量。之后在每次调用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的坑的地方,在实际编程中可以少走点弯路。如果觉得有用的话,欢迎点个赞或者关注哦。谢谢。

你可能不知道的14个javascript调试技巧(代码片段)

...花费更少的时间来解决这些错误。我们已经列出了14个您可能不知道的调试技巧,但可能要记住,这样下次你需要调试JavaScript代码时就可以马上使用了!现在就马上开始。1.‘debugger;’除了 console 查看详情

你可能不知道的docker命令的奇淫怪巧(代码片段)

原文:你可能不知道的docker命令的奇淫怪巧你可能不知道的docker命令的奇淫怪巧Intro介绍并收录一些可能会用到的一些简单实用却很少有人用的docker命令danglingimagesbuild自己的docker镜像的时候,有时会遇到用一个甚至多个中间层镜... 查看详情

你可能不知道的一些gopackages知识(代码片段)

关于GoPackage关于GoPackageGoPackages主要用来把相关的functions,variables,和constants组织到一起,这样你就可以很方便的迁移Packages和把它们用到自己的程序中。注意除了mainpackage,Gopackages不是自治程序,不能被编译成可执行文件。它们必... 查看详情

spring中你可能不知道的事(代码片段)

...了,是众多Java开发大神的结晶,很多功能,很多细节,可能一辈子都不会用到,不会发现,作为普通开发的我们,只能尽力去学习,去挖掘,也许哪天可以用到呢。让我们进 查看详情

你可能不知道的viewport(代码片段)

概述前几天偶然看到一个pc端网页,发现用手机打开竟然同比缩放了,作为一个前端从业者,我自然想要弄清它到底是怎么缩放的。之后查了它的meta信息,css和js,发现没有任何兼容手机端的代码,那它到底是怎么缩放的呢?百... 查看详情

你可能不知道的javascript代码片段和技巧(下)(代码片段)

JavaScript是一个绝冠全球的编程语言,可用于Web开发、移动应用开发(PhoneGap、Appcelerator)、服务器端开发(Node.js和Wakanda等等。JavaScript还是很多新手踏入编程世界的第一个语言。既可以用来显示浏览器中的简单提示框,也可以通... 查看详情

你可能不知道的javascript代码片段和技巧(上)(代码片段)

JavaScript是一个绝冠全球的编程语言,可用于Web开发、移动应用开发(PhoneGap、Appcelerator)、服务器端开发(Node.js和Wakanda等等。JavaScript还是很多新手踏入编程世界的第一个语言。既可以用来显示浏览器中的简单提示框,也可以通... 查看详情

你可能知道事务的四大特性,但是你不一定知道事务的实现原理(代码片段)

说到数据库,那就一定会聊到事务,事务也是面试中常问的问题,我们先来一个面试场景:面试官:"事务的四大特性是什么?"我:"ACID,即原子性(Atomicity)、隔离性(Isolation)、持久性(Durability)、一致性(Consistency)!"面试官:"在MySQL数据... 查看详情

你可能不知道的9条webpack优化策略(代码片段)

引言webpack的打包优化一直是个老生常谈的话题,常规的无非就分块、拆包、压缩等。本文以我自己的经验向大家分享如何通过一些分析工具、插件以及webpack新版本中的一些新特性来显著提升webpack的打包速度和改善包体积,学会... 查看详情

你可能不知道的new.target(代码片段)

new是构造函数生成实例的命令,ES6为new命令引入了new.target属性。这个属性用于确定构造函数是怎么调用的。在构造函数中,如果一个构造函数不是通过new操作符调用的,new.target会返回undefined。使用场景如果一个构造函数不通过new命... 查看详情

初学者可能不知道的vue技巧(代码片段)

...些许便利,如有理解错误,请纠正。技巧/坑点1.setTimeout/setInterval场景一:this指向改变无法用this访问vue实例mounted()setTimeout(function()//setInterval同理console.log(this);//此时this指向Window对象,1000);解决方法:使用箭头函数或者缓存this//箭... 查看详情

你可能不知道的idea高级调试技巧(代码片段)

一、条件断点循环中经常用到这个技巧,比如:遍历1个大List的过程中,想让断点停在某个特定值。参考上图,在断点的位置,右击断点旁边的小红点,会出来一个界面,在Condition这里填入断点条件即可,这样调试时,就会自动... 查看详情

axios你可能不知道使用方式(代码片段)

 在前端日常开发中除了纯静态展示页面,必不可少的就是做一些接口请求,从XMLHttpRequest,到jQuery的ajax,再到后来的Fetch和Axios。为什么选择Axios从浏览器中创建XMLHttpRequests从node.js创建http请求支持PromiseAPI拦截请求和响应转换... 查看详情

你可能不知道的前端知识点(代码片段)

新建了一个repo:justjavac/the-front-end-knowledge-you-may-dont-know发掘被我们忽略的前端知识点。所有的讨论以issues的形式进行,任何人都可以在issues区围观讨论。本repo的目的在于搜集、讨论,最终的内容会整理成文章、PPT、PDF发布在小... 查看详情

可能你不知道的,关于自动装箱和自动拆箱(代码片段)

包装类我们知道,Java中包含了8种基本数据类型:整数类型:byte、short、int、long字符类型:char浮点类型:float、double布尔类型:boolean这8种基本数据类型的变量不需要使用new来创建,它们不会在堆上创建,而是直接在栈内存中存... 查看详情

java8中你可能不知道的一些地方之stream实战(代码片段)

Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。Optional对象构建&值获取方法实例代码如下Optional<Str... 查看详情

java8中你可能不知道的一些地方之optional实战(代码片段)

Optional<T>类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。Optional对象构建&值获取方法实例代码如下Optional<Str... 查看详情

c++内存字节对齐与位域你可能不知道的c++(代码片段)

内存字节对齐什么是内存对齐呢,先来看一个对比#include<iostream>usingnamespacestd;#pragmapack(show)//16structPackAchara;intb;shortc;;structPackBintb;chara;shortc;;intmain()cout<<sizeof(PackA)<<endl;/ 查看详情