• setTimeout 和 setInterval 的区别及相互模拟


    这几天用到了 setinterval(),但是发现 setInterval 有缺点,都建议用 setTimeout 模拟 setInterval,也可理解为链式的 setTimeout。所以这里总结一下settimeout()和setinterval()的区别,以及它们之间的相互模拟。

    一、setTimeout 和 setInterval 的区别

    setTimeout() 定义和用法:

    定义:

    setTimeout()方法用于在指定毫秒数后再调用函数或者计算表达式(以毫秒为单位)

    语法:

    setTimeout(code,millisec)

    code:必需,要调用的函数后要执行的 JavaScript 代码串;millisec:必需,在执行代码前需等待的毫秒数。

    setTimeout() 只执行函数一次,如果需要多次调用可以使用 setInterval(),或者在函数体内再次调用setTimeout()

    示例代码:延迟1秒弹出 Hello

    // 延迟1秒弹出 Hello
    setTimeout(function(){ 
      alert("Hello");
    }, 1000);

    setInterval() 定义和用法:

    定义:

    setInterval() 方法用于按照指定的周期(以毫秒计)来循环调用函数或计算表达式,直到 clearInterval() 被调用或窗口关闭,由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。

    语法:

    setInterval(code,millisec[,"lang"])

    code:必需,要调用的函数或要执行的JavaScript 代码串;millisec:必须,周期性执行或调用 code 之间的时间间隔,以毫秒计。

    setInterval() 会不停的调用函数,直到clearInterval() 被调用或者窗口被关闭,由 setInterval() 返回的ID值可用作 clearInterval() 方法的参数。

    示例代码:一直显示当前时间,点击停止不继续

    <p>显示当前时间:</p>
    <p id="demo"></p>
    <button onclick="myStopFunction()">停止时间</button>
    <!-- 一直显示当前时间,点击停止不继续 -->
    <script>
    var myVar = setInterval(function(){ myTimer() }, 1000);
    
    function myTimer() {
        var d = new Date();
        var t = d.toLocaleTimeString();
        document.getElementById("demo").innerHTML = t;
    }
    
    function myStopFunction() {
        clearInterval(myVar);
    }
    </script>

    区别总结:

    setTimeout() 方法只运行一次,也就是说当达到设定的时间后就开始运行指定的代码,运行完后就结束了,次数是一次。

    setInterval() 是循环执行的,即每达到指定的时间间隔就执行相应的函数或者表达式,只要窗口不关闭或 clearInterval() 调用就会无限循环下去。

    二、知识储备

    1、浏览器是个多进程应用
    首先你要知道 浏览器是个多进程应用,那这些进程里都包含哪些:

    • Browser进程:浏览器的主进程(负责协调、主控),只有一个,作用:
      • 负责浏览器界面显示,与用户交互。如前进,后退等
      • 负责各个页面的管理,创建和销毁其他进程
      • 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
      • 网络资源的管理,下载等
    • 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
    • GPU进程:最多一个,用于3D绘制等
    • 浏览器内核(浏览器渲染进程,它内部是多线程的):默认每个Tab页面一个进程,互不影响。主要作用为

    2、浏览器内核中的多线程

    • GUI渲染线程
    • JS引擎线程:负责解析Javascript脚本,运行代码。
    • 事件触发线程:归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
      当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中。
      由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
    • 定时触发器线程:因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确。计时完毕后,添加到事件队列中,等待JS引擎空闲后执行。
    • 异步http请求线程

    三、setInterval缺点

    定时器指定的时间间隔,表示的是何时将定时器的代码添加到消息队列,而不是何时执行代码。所以真正何时执行代码的时间是不能保证的,取决于何时被主线程的事件循环取到,并执行。

    setInterval(function, N)  
    //即:每隔N秒把function事件推到消息队列中

    上图可见,setInterval每隔100ms往队列中添加一个事件;100ms后,添加T1定时器代码至队列中,主线程中还有任务在执行,所以等待,some event执行结束后执行T1定时器代码;又过了100ms,T2定时器被添加到队列中,主线程还在执行T1代码,所以等待;又过了100ms,理论上又要往队列里推一个定时器代码,但由于此时T2还在队列中,所以T3不会被添加结果就是此时被跳过;这里我们可以看到,T1定时器执行结束后马上执行了T2代码,所以并没有达到定时器的效果。

    综上所述,setInterval有两个缺点:

    • 使用setInterval时,某些间隔会被跳过;即使setInterval调用的方法报错了,他仍然会继续执行。
    • 无视网络延迟,可能多个定时器会连续执行

    可以这么理解:每个setTimeout产生的任务会直接push到任务队列中;而setInterval在每次把任务push到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中)

    因而我们一般用 setTimeout 模拟 setInterval,来规避掉上面的缺点。

    模拟 setInterval() :

    将 setTimeout() 包含在一个执行函数A中,而setTimeout() 自己的code执行函数又是A,然后在函数A外将函数A执行一次,即达到了循环执行的目的。

    setTimeout(function () {
        // 任务
        setTimeout(arguments.callee, 1000);
    }, 1000)

    上述函数每次执行的时候都会创建一个新的定时器,第二个 setTimeout 使用了 arguments.callee() 获取当前函数的引用,并且为其设置另一个定时器。

    好处:

    • 在前一个定时器执行完前,不会向队列插入新的定时器(解决缺点一)
    • 保证定时器间隔(解决缺点二)

    示例代码:

    var intervalNum = 0;
    function testsetInterval() {
        var date = new Date();
        console.log(date.getSeconds());
        console.log("setInterval", intervalNum++);
    }
    function recursive() {
        testsetInterval();
        setTimeout(function () {
            recursive()      //递归,每隔4秒调用一次recursive()
        }, 4000)
    }
    function testFuntion() {
        recursive();        //在方法recursive外,调用一次recursive,以启动循环调用!
    }

    实现循环执行,和 setInterval() 功能相同。

    当然也可以用 setInterval() 来模拟 setTimeout() ,具体使用那个,以具体的需求和场景具体分析,就像for循环可以模拟所有的循环一样(包括分支,以及do while一样)。一般情况下 setTimeout() 用于延迟执行某方法或功能;setInterval() 则一般用于刷新表单,对于一些表单的假实时指定时间刷新同步。

    模拟 setTimeout() :

    用 setInterval() 模拟 setTimeout() 很简单,在 setInterval() 执行一次后,立刻关闭窗口(当然这是耍无赖)或者执行 clearInterval() 方法(这个靠谱点)。clearInterval() 需要在 setInterval()执行code方法内或其他地方执行,不能紧接着 setInterval() 后面执行,那样setInterval() 还没等到执行,就已经被干掉了。

    示例代码:

    var intervalNum = 0, clearId = 0;
    function testsetInterval(){
        var date = new Date();
        console.log(date.getSeconds());
        console.log("setInterval", intervalNum++);
        clearInterval(clearId);     //也可以在此执行
    }
    function testFuntion() {
        clearId = setInterval(function () {
            testsetInterval();          //每隔4秒调用testsetInterval()
            // clearInterval(clearId);     //可以在此执行
        },4000);
    }

    执行一次,关闭 setInterval(),和 setTimeout() 功能相同

    最后,解释为什么 “ 建议传入函数而不是字符串以作为第一个参数”。 

    setTimeout()、setInterval() 允许传入一个JS代码字符串并执行,然而在JS代码中执行另一段JS代码时,代码首先会以正常的方式求值,然后在执行过程中对包含于字符串中的代码发起另一个求值运算,从而造成双重求值。它比直接包含的代码执行速度慢很多,原因在于, 每次调用setTimeout()、setInterval() 都会创建一个新的解释器/编译器实例。这必然使得代码执行速度变慢,效率降低,从而造成性能的浪费。所以建议传入函数而不是字符串来作为第一个参数。

    实例

    下面写了一个小例子:点击 start 开始旋转,点击 stop 停止旋转。 

    先是用 setInterval() 实现的:

    <div class="divOne">
        123
      </div>
      <button id="click">start</button>
      <button id="stop">stop</button>
      <script src="./js/jquery-1.8.3.min.js"></script>
      <script src="./js/jquery.rotate.min.js"></script>
      <script>
        var angle = 0;
        var myVar;
        $('#click').on('click', function(){
          myVar = setInterval(function(){ myTimer() }, 10);
        })
        function myTimer() {
            angle += 3;
            $('.divOne').rotate(angle);
        }
        $("#stop").on('click', function(){
          clearInterval(myVar)
        })
       </script>

    然后用 setTimeout 来模拟 setInterval 实现:

        var angle = 0;
        $('#click').on('click', function(){
          setTimeout(myTimer, 10)
        })
        var myTimer = function() {
          setTimeout(myTimer, 10)
          angle += 3;
          $('.divOne').rotate(angle);
        }
        $("#stop").on('click', function(){
          console.log(111)
          myTimer = null;
        })
    
    // 或者
    
        var angle = 0;
        var stop = false;
        $('#click').on('click', function(){
          stop = false;
          setTimeout(function () {
            angle += 3;
            $('.divOne').rotate(angle);
            var myTimer = setTimeout(arguments.callee, 10); 
            if( stop ){//符合条件 删除定时器
              clearTimeout(myTimer);
            }    
          }, 10);
        })
        $("#stop").on('click', function(){
          console.log(111)
          stop = true;
        })

    可以实现同样的功能。

  • 相关阅读:
    [转]C#、VB.NET使用HttpWebRequest访问https地址(SSL)的实现
    C#设置System.Net.ServicePointManager.DefaultConnectionLimit,突破Http协议的并发连接数限制
    [转]WebBrowser控件禁用超链接转向、脚本错误提示、默认右键菜单和快捷键
    [转]C#打印DataGridView的例子源码
    c# TreeView 父节点选中/不选时子节点都同步选中/不选
    C#中PictureBox异步加载图片
    [转]FusionCharts 3.1 破解版 – 非常好用的Flash图表控件
    配合JavaScript拖动页面中控件
    在ThinkPad T400上安装win2003 所遇问题
    C# 抛弃MoveTo来实现文件重命名
  • 原文地址:https://www.cnblogs.com/joe235/p/13402282.html
Copyright © 2020-2023  润新知