JavaScript - 定时器 - 学习/实践

1.应用场景

主要用于定时执行代码.

应用场景:

1.防抖动,

2.调整事件的发生顺序, 用户自定义的回调函数,通常在浏览器的默认动作之前触发, 

3.将任务放到浏览器最早可得的空闲时段执行,所以用于那些计算量大、耗时长的任务,

4.代码高亮的处理

5.实现网页动画

6.实现轮询。

2.学习/操作

1.介绍

JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()setInterval()这两个函数来完成。

它们向任务队列添加定时任务

 

2.setTimeout

setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。

var timerId = setTimeout(func|code, delay);

上面代码中,setTimeout函数接受两个参数,第一个参数func|code是将要推迟执行的函数名或者一段代码,第二个参数delay是推迟执行的毫秒数。

console.log(1);
setTimeout('console.log(2)',1000);  //这里不论设置多久, 哪怕是0 , 也是后执行 console.lo(2)
console.log(3);
// 1
// 3
// 2

上面代码会先输出1和3,然后等待1000毫秒再输出2。

注意,console.log(2)必须以字符串的形式,作为setTimeout的参数。//目前Chrome已经不支持这种方式, 因为认为不安全 

版本 81.0.4044.138(正式版本) (64 位)

 

如果推迟执行的是函数,就直接将函数名,作为setTimeout的参数。//推荐写法

function f() {
  console.log(2);
}

setTimeout(f, 1000);

setTimeout的第二个参数如果省略,则默认为0。

setTimeout(f)
// 等同于
setTimeout(f, 0)

除了前两个参数,setTimeout还允许更多的参数。它们将依次传入推迟执行的函数(回调函数)。

setTimeout(function (a,b) {
  console.log(a + b);
}, 1000, 1, 1);

输出结果: 3

上面代码中,setTimeout共有4个参数。最后那两个参数,将在1000毫秒之后回调函数执行时,作为回调函数的参数。

还有一个需要注意的地方,

如果回调函数是对象的方法,那么setTimeout使得方法内部的this关键字指向全局环境,而不是定义时所在的那个对象。

var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(obj.y, 1000) // 1

上面代码输出的是1,而不是2。

因为当obj.y在1000毫秒后运行时,this所指向的已经不是obj了,而是全局环境。

 

为了防止出现这个问题,一种解决方法是将obj.y放入一个函数。

var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(function () {
  obj.y();
}, 1000);
// 2

上面代码中,obj.y放在一个匿名函数之中,这使得obj.yobj的作用域执行,而不是在全局作用域内执行,所以能够显示正确的值。

另一种解决方法是,使用bind方法,将obj.y这个方法绑定在obj上面。

var x = 1;

var obj = {
  x: 2,
  y: function () {
    console.log(this.x);
  }
};

setTimeout(obj.y.bind(obj), 1000)
// 2

3.setInterval

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

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

上面代码中,每隔1000毫秒就输出一个2,会无限运行下去,直到关闭当前窗口。

setTimeout一样,除了前两个参数,setInterval方法还可以接受更多的参数,它们会传入回调函数。

 

下面是一个通过setInterval方法实现网页动画的例子。

var div = document.getElementById('someDiv');
var opacity = 1;
var fader = setInterval(function() {
  opacity -= 0.1;
  if (opacity >= 0) {
    div.style.opacity = opacity;
  } else {
    clearInterval(fader);
  }
}, 100);

上面代码每隔100毫秒,设置一次div元素的透明度,直至其完全透明为止。

 

setInterval的一个常见用途是实现轮询。下面是一个轮询 URL 的 Hash 值是否发生变化的例子。

var hash = window.location.hash;
var hashWatcher = setInterval(function() {
  if (window.location.hash != hash) {
    updatePage();
  }
}, 1000);

Note:

setInterval指定的是“开始执行”之间的间隔,并不考虑每次任务执行本身所消耗的时间。

因此实际上,两次执行之间的间隔会小于指定的时间。

比如,setInterval指定每 100ms 执行一次,每次执行需要 5ms,那么第一次执行结束后95毫秒,第二次执行就会开始。

如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。

 

为了确保两次执行之间有固定的间隔,可以不用setInterval,而是每次执行结束后,使用setTimeout指定下一次执行的具体时间

var i = 1;
var timer = setTimeout(function f() {
  // ...
  timer = setTimeout(f, 2000);
}, 2000);

上面代码可以确保,下一次执行总是在本次执行结束之后的2000毫秒开始。

Note: 上面的code即替换setInterval

 

4.clearTimeout(),clearInterval()

setTimeoutsetInterval函数,都返回一个整数值,表示计数器编号。

var id1 = setTimeout(f, 1000);
var id2 = setInterval(f, 1000);

clearTimeout(id1);
clearInterval(id2);

上面代码中,回调函数f不会再执行了,因为两个定时器都被取消了。

setTimeoutsetInterval返回的整数值是连续的,也就是说,第二个setTimeout方法返回的整数值,将比第一个的整数值大1。

 

Note:  setTimeoutsetInterval 两者之间返回的ID值是在相互基础上交叉+1的

截图:

 

利用这一点,可以写一个函数,取消当前所有的setTimeout定时器。//TBD

(function() {
  // 每轮事件循环检查一次
  var gid = setInterval(clearAllTimeouts, 0);

  function clearAllTimeouts() {
    var id = setTimeout(function() {}, 0);
    while (id > 0) {
      if (id !== gid) {
        clearTimeout(id);
      }
      id--;
    }
  }
})();

上面代码中,先调用setTimeout,得到一个计算器编号,然后把编号比它小的计数器全部取消。

 

5.实例:debounce 函数  //防抖动

https://wangdoc.com/javascript/async/timer.html#实例:debounce-函数

code:


<!DOCTYPE html>
<html>
<head>
    <title>防抖动</title>
    <style type="text/css">
        textarea{
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>
<div id="someDiv">hello world</div>
<textarea></textarea>

<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
(function(){
    //console.log(window);
    //var dom = $('#someDiv');
    //console.log(dom);
    //return;
    $('textarea').on('keydown', debounce(ajaxAction, 2500)); //防抖动间隔为2.5s  可以将数值改为0 效果更佳明显

    function debounce(fn, delay){
      var timer = null; // 声明计时器
      return function() {
        var context = this;
        var args = arguments;
        // console.log(args)
        clearTimeout(timer);
        timer = setTimeout(function () {
          fn.apply(context, args);
        }, delay);
      };
    }

    function ajaxAction(para){
        console.log(para, 'ajaxAction');
    }
})()

</script>
</body>
</html>

6.运行机制  

https://wangdoc.com/javascript/async/timer.html#运行机制

 

7.setTimeout(f, 0)   //应用场景有一些, 建议练习一下

https://wangdoc.com/javascript/async/timer.html#settimeoutf-0

code:


<!DOCTYPE html>
<html>
<head>
    <title>可以调整事件的发生顺序/用户自定义的回调函数,通常在浏览器的默认动作之前触发</title>
    <style type="text/css">
        textarea{
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>
<div id="someDiv">hello world</div>
<input type="button" id="myButton" value="click">
<input type="text" id="input-box">

<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
(function(){

    //1.可以调整事件的发生顺序[这里目标是先执行父级元素的事件,然后执行子元素的事件]
    // var input = document.getElementById('myButton');

    // input.onclick = function A() {
    //   setTimeout(function B() {
    //     input.value +=' input';
    //   }, 0)
    // };

    // document.body.onclick = function C() {
    //   input.value += ' body'  //先执行父级元素的事件
    // };


    //2.用户自定义的回调函数,通常在浏览器的默认动作之前触发  -- 想在用户每次输入文本后,立即将字符转为大写
    document.getElementById('input-box').onkeypress = function (event) {
      //this.value = this.value.toUpperCase();  //这种写法, 只能让前面输入的字母大写, 最后一个可能还是输入时的样子

      //改写如下:
      var self = this;
      setTimeout(function() {
        self.value = self.value.toUpperCase();
      }, 0);
    }

    //3.那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f, 0)里面执行
    var div = document.getElementsByTagName('div')[0];
    // console.log(div);

    // 写法一 //会看到阻塞 浏览器刷新图标一直在打转
    // for (var i = 0xFFFFFF; i > 0xA00000; i--) {  
    //   div.style.backgroundColor = '#' + i.toString(16); //可以转换为 rgb(160, 0, 1)形式
    // }


    // 写法二  //会看到没有阻塞 浏览器刷新图标没有打转, 而且可以看到页面上颜色的渐变效果
    var timer;
    var i=0x100000;

    function func() {
      timer = setTimeout(func, 0);
      div.style.backgroundColor = '#' + i.toString(16);
      if (i++ == 0xFFFFFF) clearTimeout(timer);
    }

    timer = setTimeout(func, 0);


    //4.另一个使用这种技巧的例子是代码高亮的处理。如果代码块很大,一次性处理,可能会对性能造成很大的压力,那么将其分成一个个小块,一次处理一块,比如写成setTimeout(highlightNext, 50)的样子,性能压力就会减轻。

})()

</script>
</body>
</html>

后续补充

...

3.问题/补充

TBD

4.参考

https://wangdoc.com/javascript/async/timer.html  //阮一峰 - 定时器 

后续补充

...

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章