你所不知道的setInterval

你所不知道的setTimeout记载了下setTimeout相关,此篇则整理了下setInterval;做为拥有普遍应用场景(定时器,轮播图,动画效果,自动滚动等等),而又充满各类不肯定性的这setInterval,自当先洞悉它,才能很好的驾驭它。javascript

如觉此处样式排版不尽人意,可移步首链晚晴幽草轩~你所不知道的setIntervalhtml

1. setInterval()基础

setInterval函数的用法与setTimeout彻底一致,区别仅仅在于setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。java

<input type="button" onclick="clearInterval(timer)" value="stop">

<script>
  var i = 1
  var timer = setInterval(function() {
    console.log(2);
  }, 1000);
</script>

上面代码表示每隔1000毫秒就输出一个2,直到用户点击了中止按钮。浏览器

与setTimeout同样,除了前两个参数,setInterval 方法还能够接受更多的参数,它们会传入回调函数,下面是一个例子。函数

function f(){
  for (var i=0;i<arguments.length;i++){
    console.log(arguments[i]);
  }
}

setInterval(f, 1000, "Hello World");
// Hello World
// Hello World
// Hello World
// ...

若是网页不在浏览器的当前窗口(或tab),许多浏览器限制setInteral指定的反复运行的任务最多每秒执行一次。oop

setInterval指定的是“开始执行”之间的间隔,并不考虑每次任务执行自己所消耗的事件。所以实际上,两次执行之间的间隔会小于指定的时间。好比,setInterval指定每100ms执行一次,每次执行须要5ms,那么第一次执行结束后95毫秒,第二次执行就会开始。若是某次执行耗时特别长,好比须要105毫秒,那么它结束后,下一次执行就会当即开始。性能

var i = 1;
var timer = setInterval(function() {
  alert(i++);
}, 2000);

上面代码每隔2000毫秒,就跳出一个alert对话框。若是用户一直不点击“肯定”,整个浏览器就处于“堵塞”状态,后面的执行就一直没法触发,将会累积起来。举例来讲,第一次跳出alert对话框后,用户过了6000毫秒才点击“肯定”,那么第二次、第三次、第四次执行将累积起来,它们之间不会再有等待间隔。动画

为了确保两次执行之间有固定的间隔,能够不用setInterval,而是每次执行结束后,使用setTimeout指定下一次执行的具体时间。上面代码用setTimeout,能够改写以下。线程

var i = 1;
var timer = setTimeout(function() {
  alert(i++);
  timer = setTimeout(arguments.callee, 2000);
}, 2000);

上面代码能够确保两次执行的间隔是2000毫秒。code

根据这种思路,能够本身部署一个函数,实现间隔时间肯定的setInterval的效果。

function interval(func, wait){
  var interv = function(){
    func.call(null);
    setTimeout(interv, wait);
  };

  setTimeout(interv, wait);
}

interval(function(){
  console.log(2);
},1000);

上面代码部署了一个interval函数,用循环调用setTimeout模拟了setInterval。

HTML 5标准规定,setInterval的最短间隔时间是10毫秒,也就是说,小于10毫秒的时间间隔会被调整到10毫秒。

2. setInterval运行机制

setInterval的运行机制是,将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。若是到了,就执行对应的代码;若是不到,就等到再下一轮Event Loop时从新判断。这意味着,setTimeout指定的代码,必须等到本次执行的全部代码都执行完,才会执行。

每一轮Event Loop时,都会将“任务队列”中须要执行的任务,一次执行完。setTimeout和setInterval都是把任务添加到“任务队列”的尾部。所以,它们实际上要等到当前脚本的全部同步任务执行完,而后再等到本次Event Loop的“任务队列”的全部任务执行完,才会开始执行。因为前面的任务到底须要多少时间执行完,是不肯定的,因此没有办法保证,setTimeout和setInterval指定的任务,必定会按照预约时间执行。

这一点对于setInterval影响尤为大。

setInterval(function(){
  console.log(2);
},1000);

(function (){
  sleeping(3000);
})();

上面的第一行语句要求每隔1000毫秒,就输出一个2。可是,第二行语句须要3000毫秒才能完成,请问会发生什么结果?

结果就是等到第二行语句运行完成之后,马上连续输出三个2,而后开始每隔1000毫秒,输出一个2。也就是说,setIntervel具备累积效应,若是某个操做特别耗时,超过了setInterval的时间间隔,排在后面的操做会被累积起来,而后在很短的时间内连续触发,这可能或形成性能问题(好比集中发出Ajax请求)。

为了进一步理解JavaScript的单线程模型,请看下面这段伪代码。

function init(){
  { 耗时5ms的某个操做 }
  触发mouseClickEvent事件
  { 耗时5ms的某个操做 }
  setInterval(timerTask,10);
  { 耗时5ms的某个操做 }
}

function handleMouseClick(){
  耗时8ms的某个操做
}

function timerTask(){
  耗时2ms的某个操做
}
init();

请问调用init函数后,这段代码的运行顺序是怎样的?

0-15ms:运行init函数。
15-23ms:运行handleMouseClick函数。请注意,这个函数是在5ms时触发的,应该在那个时候就当即运行,可是因为单线程的关系,必须等到init函数完成以后再运行。
23-25ms:运行timerTask函数。这个函数是在10ms时触发的,规定每10ms运行一次,即在20ms、30ms、40ms等时候运行。因为20ms时,JavaScript线程还有任务在运行,所以必须延迟到前面任务完成时再运行。
30-32ms:运行timerTask函数。
40-42ms:运行timerTask函数。

对于setInterval得使用,我的建议是能不用尽可能不用。涉及到必需要的定时器,前文已经叙述可使用两个setTimeout嵌套组合来实现,而且还能规避掉一些问题得发生。涉及到要用它来制做动画( jQuery就使用setInterval来写动画,也是致使其慢缘由之一),更建议使用requestAnimationFrame(RAF),或者直接采用CSS来写(若是能够的话)。

requestAnimationFrame比起setTimeout、setInterval的优点主要有两点:

  1. requestAnimationFrame会把每一帧中的全部DOM操做集中起来,在一次重绘或回流中就完成,而且重绘或回流的时间间隔牢牢跟随浏览器的刷新频率,通常来讲,这个频率为每秒60帧。
  2. 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这固然就意味着更少的的cpu,gpu和内存使用量。

参考文章连接:
阮一峰-javaScript标准参考教程


您可能会感兴趣的文章:
你所不知道的setTimeout

相关文章
相关标签/搜索