「JavaScript 定时器」setInterval、setTimeout和requestAnimationFrame浅析

6

一.常见定时器

我们常见的定时器有以下两种

1. window.setTimeout 用于在指定的毫秒数后执行某段既定的代码
2. window.setInterval 用于每隔一段毫秒数重复执行既定的代码

这两个方法都可以通过手工设置时间来设定是多少毫秒后执行这段代码,或者是每隔多少毫秒执行这段代码。  

虽然我们期待浏览器按照我们设定的时间精确的执行代码,但是js却不能保证代码能恰好在那个时间点被运行,原因有两个。

  • 大多数浏览器并没有精确到毫秒级别的触发事件,例如,我们设定某个函数在3毫秒后执行,在老版本的IE中,这个函数至少会在15毫秒以后执行。而在现代浏览器中,这个数值会短一点,但时间差一般也会超过1毫秒。
  • 第二个原因与js的运行机制有关,具体见JavaScript 运行机制详解:再谈Event Loop.简单来说,就是js是一个单线程的解释器,一段时间只能执行一段代码,所以运行时分为主线程和任务队列两部分。而我们在定时器中设置的时间,仅代表1000毫秒后把这个任务插入到任务队列中,而此时必须要等到主线程的代码执行完毕,才能执行任务队列中的定时器的任务(在任务队列中也有调度,不一定第一个执行当前任务),因此时间是无法保证的。

二、requestAnimationFrame

那有没有时间准确的定时器呢?有一种选择是requestAnimationFrame. 示例如下:

 function animateMe(){
    requestAnimationFrame(function(){
      console.log(new Date());
      animateMe();
    })
}
animateMe();

这个api的原理是在由系统来决定回调函数的执行时机,在每一次系统绘制之前,会主动调用requestAnimationFrame中的回调函数,而频率也紧紧跟随浏览器的刷新频率。比如一般电脑的刷新频率通常为60Hz,即一秒钟重绘60次,那么回调函数就等于1000/60=16.7毫秒被执行一次,而如果刷新频率变为75Hz,那么这个时间就变为1000/75=13.3毫秒被执行一次。这样能保证回调函数在每一次绘制的间隔时间内只被执行一次,因此它的时间是可靠的。

三、实战

光说不练假把式,现在我们就用上面介绍的三种定时器完成进度条的效果。

1.setInterval

var timer;
$('.runBtn').click(function(){
    clearInterval(timer);
    $('#bar').width(0);
    timer = setInterval(function(){
      if($('#bar').width() < 500){
         $('#bar').width($('#bar').width()+5);
         $('#bar').text($('#bar').width()/5+'%');
      }else{
        clearInterval(timer);
      } 
    },16);
 });

点此预览效果

2.setTimeout

var timer;
$('.runBtn').click(function(){
    clearTimeout(timer);
    $('#bar').width(0);
    timer = setTimeout(function fn(){
      if($('#bar').width() < 500){
         $('#bar').width($('#bar').width()+5);
         $('#bar').text($('#bar').width()/5+'%');
         timer = setTimeout(fn,16);
      }else{
        clearTimeout(timer);
      } 
    },16);
 });

点此预览效果

3.requestAnimationFrame

var timer;
$('.runBtn').click(function(){
    cancelAnimationFrame(timer);
    $('#bar').width(0);
    timer = requestAnimationFrame(function fn(){
      if($('#bar').width() < 500){
         $('#bar').width($('#bar').width()+5);
         $('#bar').text($('#bar').width()/5+'%');
         requestAnimationFrame(fn);
      }else{
        cancelAnimationFrame(timer);
      } 
    });
 });

点此预览效果

四、最后

文章都来自本人的总结,难免有些纰漏,欢迎大家指正。一起学习,一起进步。如果觉得不错,欢迎点赞收藏嘤嘤嘤~~~

你可能感兴趣的

jessezhao1990 · 2018-05-21

requestAnimationFrame感觉不能和setTimeout还有setInterval直接相比较。他们相比较的前提条件是在做动画的场景下。在其他场景下requestAnimationFrame没有太多使用空间。说requestAnimationFrame是时间准确的定时器。我觉得这句话有点欠妥。因为定时器的话不管你窗口最小化不最小化。但是requestAnimationFrame在窗口最小化的时候,会暂停。

个人见解哈~ 错误之处请纠正~

+1 回复

0

你好,首先真的非常感谢你的建议!对于最小化会暂停requestAnimationFrame的事情我之前并不了解,看到你的建议之后我有去实践一下,这样看来的确不能叫做时间准确的定时器,不过本文的本意就是让大家了解一下这个api以及实现进度条的效果啦~

Jchermy 作者 · 2018-05-26
beck · 2018-04-30

$ is not defined

回复

0

有用到jquery的

Jchermy 作者 · 2018-04-30
0

谢谢大佬 哈哈 我以为是原生的了

beck · 2018-04-30
0

不谢,欢迎点赞收藏哈哈~

Jchermy 作者 · 2018-04-30
Jenny · 2018-05-02

感谢分享!章节标题建议使用 一/二/三 级标题以便生成目录索引
Markdown 语法传送门:https://segmentfault.com/mark...

回复

0

好的,下次会注意~

Jchermy 作者 · 2018-05-02
载入中...