• 定时器深入讲解


      定时器一般适用于需要定时刷新页面的地方,比如在页面做一个时钟。或者对于异步加载的情况下也可以使用,是一种思路,比如我需要给一个div设置样式或者内容,而此时该div并没有加载渲染,所有此时直接设置就会出现不生效的现象,因而可以使用定时器,延迟加载。当然,对于这种现象还有很多别的解决方法。

      定时器有timeoutinterval两种,其开启方法分别是setTimeout()setInterval(),参数有两个,第一个是要执行的回调方法函数,第二个是时间delay,时间是毫秒单位;清除方法是clearTimeout,clearInterval,参数是具体的定时器。

      一般的使用是(timeout和interval类似,以下只着重讲interval),

    var a = setInterval(function(){
        clearInterval(a);
    }, 1000);

      IE0+ 还支持回调参数的传入:

    var timeoutID = window.setTimeout(function(){}, delay, [param1, param2, ...]);
    //setTimeout和setInterval都是window对象的方法,所有平常使用并不比写window.

      两者的区别:timeout只执行一次,所以也可以不必刻意清除,而interval则是每隔设置的时间就执行一次,所有如果使用较多而又不清除,就会使页面占用较大的内存。

      有时候,业务场景需要我们开启多个不定个的定时器,而这个时候清楚定时器就不再那么直接。比如,在页面上展现N个用户的信息,且要定时刷新,且这N个用户信息刷新的接口或者方法又不同,就需要使用不同的定时器。我也曾用定时器写过一个页面小游戏,就是类似接彩色豆子那种。

      这个时候,如果刷新完了要批量清理定时器,有两个方法

      (1)强制暴力清除:

      原理:定时器在一些低版本谷歌和火狐上是从1开始的一个number(比如上边的a,控制台输入a回车后,可以看到a其实就是一个数字),当前页面有几个定时器,a就是几,而高版本的谷歌和ie上,定时器就是一个随机的number了。因而我们可以设置一个很大的数字,然后依次清除(clearInterval(具体数字)):

    1 for(var i = 1; i < 10000; i++) {
    2 clearInterval(i);
    3 }

    但是这种方法很不推荐,我也曾使用过,非常不好用而已要执行很多次。

      (2)使用数组或者对象来存储定时器,然后遍历清除:

    使用对象:

    var pageTimer = {} ; //定义计算器全局变量
    //赋值模拟
    pageTimer["timer1"] = setInterval(function(){},2000);
    pageTimer["timer2"] = setInterval(function(){},2000);
    ...
    //全部清除方法
    for(var each in pageTimer){
        clearInterval(pageTimer[each]);
    }

    使用数组:

    var pageTimer = [] ; //定义计算器全局变量
    //赋值模拟
    pageTimer.push(setInterval(function(){},2000));
    pageTimer.push(setInterval(function(){},2000));
    ...
    //全部清除方法
    $.each(pageTimer, function(index, e){
        clearInterval(e);
    })

    一般使用方法二,而不使用暴力清除。

    定时器的原理:

    javascript 的事件循环机制,导致第二个参数并不代表延迟delay毫秒之后立即执行回调函数,而是尝试将回调函数加入到事件队列。实际上,setTimeout 和 setInterval 在这一点上处理又存在区别:

    • setTimeout:延时delay毫秒之后,直接将回调函数加入事件队列。

    • setInterval: 延时delay毫秒之后,先看事件队列中是否存在还没有执行的回调函数(setInterval的回调函数),如果存在,就不要再往事件队列里加入回调函数了。

    所以,当我们的代码中存在耗时的任务时,定时器并不会表现的如我们所想的那样。

    举例:

     1 function now() {
     2   return new Date();  
     3 }
     4 var time1 = now();
     5 setTimeout(function () {
     6   console.log('第一个timeout回调执行等待时间:', now() - time1);
     7 
     8   var time2 = now();
     9   setTimeout(function () {
    10     console.log('第二个timeout回调执行等待时间:', now() - time2);
    11   }, 100);
    12 }, 100);
    13 
    14 //控制台打印
    15 //第一个timeout回调执行等待时间: 103
    16 //第二个timeout回调执行等待时间: 100

    可以看到时间间隔并不一定等于设置的delay 100毫秒。

    在此基础上:

    var timerStart1 = now();
    setTimeout(function () {
      console.log('第一个setTimeout回调执行等待时间:', now() - timerStart1);
    
      var timerStart2 = now();
      setTimeout(function () {
        console.log('第二个setTimeout回调执行等待时间:', now() - timerStart2);
      }, 100);
    
      heavyTask();  // 耗时任务
    }, 100);
    
    var loopStart = now();
    heavyTask(); // 耗时任务
    console.log('heavyTask耗费时间:', now() - loopStart);
    
    function heavyTask() {
      var s = now();
      while(now() - s < 1000) {
      }
    }
    
    //控制台输出
    //heavyTask耗费时间: 1000
    //undefined
    //VM424:3 第一个setTimeout回调执行等待时间: 1001
    //VM424:7 第二个setTimeout回调执行等待时间: 1002

    事情的经过:

    1. 首先,第一个耗时任务(heavyTask())开始执行,它需要大约 1000ms 才能执行完毕。

    2. 从耗时任务开始执行,过了 100ms, 第一个 setTimeout 的回调函数期望执行,于是被加入到事件队列,但是此时前面的耗时任务还没执行完,所以它只能在队列中等待,直到耗时任务执行完毕它才开始执行,所以结果中我们开的看到的是: 第一个setTimeout回调执行等待时间: 1018

      3.第一个 setTimeout 回调一执行,又开启了第二个 setTimeout, 这个定时器也是期望延时 100ms 之后能够执行它的回调函数。 但是,在第一个 setTimeout 又存在一个耗时任务,所有它的剧情跟第一个定时器一样,也等待了 1000ms 才开始执行。

    可以用下面的图来概括:

    setTimeout

    再来看 setInterval 的一个例子:

     1 var intervalStart = now();
     2 setInterval(function () {
     3   console.log('interval距定义定时器的时间:', now() - loopStart);
     4 }, 100);
     5 
     6 var loopStart = now();
     7 heavyTask();
     8 console.log('heavyTask耗费时间:', now() - loopStart);
     9 
    10 function heavyTask() {
    11   var s = now();
    12   while(now() - s < 1000) {
    13   }
    14 }

    控制台打印:

    heavyTask耗费时间: 1000
    undefined
    VM426:3 interval距定义定时器的时间: 1000
    VM426:3 interval距定义定时器的时间: 1100
    VM426:3 interval距定义定时器的时间: 1201
    VM426:3 interval距定义定时器的时间: 1300
    VM426:3 interval距定义定时器的时间: 1402
    VM426:3 interval距定义定时器的时间: 1501

    ...

    上面这段代码,我们期望每隔 100ms 就打出一条日志。相对于 setTimeout 的区别, setInterval 在准备把回调函数加入到事件队列的时候,会判断队列中是否还有未执行的回调,如果有的话,它就不会再往队列中添加回调函数。 不然,会出现多个回调同时执行的情况。

    可以用下面的图来概括:
    setInterval

    想更深入理解原理可参考:

    https://www.jianshu.com/p/61d42574bf0d

    FIGHTING
  • 相关阅读:
    Linux-配置共享目录
    Linux-expect脚本-编写一个expect脚本
    MySQL-Linux下安装
    ETL-拉链算法-1
    ETL-拉链算法-带删除的拉链算法
    postgresql-基础-1
    Oracle-sql*plus
    【剑指offer11二进制中1的个数】
    【剑指offer10 矩形覆盖】
    【剑指offer080 09 跳台阶、变态跳台阶】
  • 原文地址:https://www.cnblogs.com/ljwsyt/p/9596675.html
Copyright © 2020-2023  润新知