ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

setTimeout 和 setInterval 的区别及相互模拟

2020-07-30 11:32:47  阅读:293  来源: 互联网

标签:function 执行 setInterval 代码 定时器 setTimeout 模拟


这几天用到了 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(function () {
        myTimer()
        setTimeout(arguments.callee, 10);
      }, 10)
    })
    function myTimer() {
      angle += 3;
      $('.divOne').rotate(angle);
    }
    $("#stop").on('click', function(){
      console.log(111)
      myTimer = null;
    })

可以实现同样的功能。

标签:function,执行,setInterval,代码,定时器,setTimeout,模拟
来源: https://www.cnblogs.com/joe235/p/13402282.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有