JavaScript 提供定时执行代码的功能,叫做 定时器(timer)。

主要由 setTimeout() setInterval() 这两个函数来完成。它们向任务队列添加定时任务

  • setTimeout()
        • var timerId = setTimeout( func()|code, delay);
        • 第一个参数 func() | code 是将要推迟执行的函数名或者一段代码

        • 第二个参数 delay是推迟执行的毫秒数

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

      • console.log(1);
        setTimeout('console.log(2)',1000);
        console.log(3);
        //
        //
        //
      • 上面代码会 先输出 1 和 3,然后等待 1000毫秒 再输出 2

    • 如果第一个参数

      • 是代码,则必须以字符串的形式传入
      • 是函数,则直接传入函数名
        • function f() {
          console.log(2);
          } setTimeout(f, 1000);
    • 如果第二个参数省略,则默认为 0
    • 除了前两个参数,setTimeout(func , delayTime , argu1, argu2......) 还允许更多的参数。作为 回调函数的实参
      • setTimeout(function (a,b) {
        console.log(a + b);
        }, 1000, 1, 1); // 1秒钟后打印 '2'
    • 如果传入的是 某个对象的方法,在 setTimeOut() 中运行时,this 还是会指向 window
      • var x = 1;
        
        var obj = {
        x: 2,
        y: function () {
        console.log(this.x);
        }
        }; setTimeout(obj.y, 1000); // 1 打印的是全局变量 x
    • 上面代码的解决办法是,使用匿名函数包裹
      • var x = 1;
        
        var obj = {
        x: 2,
        y: function () {
        console.log(this.x);
        }
        }; setTimeout(function () {
        obj.y();
        }, 1000); // 2 打印的是 obj 对象的属性 x
    • 另一种解决方法是使用 bind() 函数将 obj.y() 方法绑定在 obj 上面

      • var x = 1;
        
        var obj = {
        x: 2,
        y: function () {
        console.log(this.x);
        }
        }; setTimeout(obj.y.bind(obj), 1000)
  • setInterval()

    • 用法与 setTimeout() 完全一致,
    • 区别在于
      • setInterval() 指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行,直到关闭当前窗口
      • setInterval() 设置的时间间隔,要把程序运行时间算在内。
        • 比如,setInterval指定每 100ms 执行一次,每次执行需要 5ms,那么第一次执行结束后95毫秒,第二次执行就会开始。
        • 如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。
      • var aDiv = document.getElementById('a_div');
        var opacity = 1;
        var flag = true; var brath = setInterval(function() {
        if (opacity >= 0 && flag ) {
        opacity -= 0.1;
        } else {
        opacity += 0.1;
        }
        aDiv.style.opacity = opacity;
        if(opacity < 0 || opacity > 1){
        flag = !flag;
        }
        }, 200);

        上面代码是让一个div 透明度慢慢降低,然后又慢慢增高。。。呼吸的效果

    • 实现轮询

      • 轮询 URL 的 Hash 值是否发生变化的例子
      • var hash = window.location.hash;
        var hashWatcher = setInterval(function() {
        if (window.location.hash != hash) {
        updatePage();
        }
        }, 1000);
    • 为了确保两次执行之间有固定的间隔
      • 可以不用 setInterval(),而是每次执行结束后,使用 setTimeout() 指定下一次执行的具体时间
      • var i = 1;
        var timer = setTimeout(function f() {
        // ...
        timer = setTimeout(f, 2000); // 可以确保,下一次执行总是在本次执行结束之后的2000毫秒开始
        }, 2000);
  • clearTimeout() clearInterval()

    • 都是返回一个整数值,表示计数器编号
    • 将该整数传入 clearTimeout() 和 clearInterval() 函数,就可以取消对应的定时器
      • var id1 = setTimeout(f, 1000);
        var id2 = setInterval(f, 1000); clearTimeout(id1);
        clearInterval(id2); // 上面代码中,回调函数f不会再执行了,因为两个定时器都被取消了
    • setTimeout() 和 setInterval() 返回的整数值是连续的,

      • 也就是说,第二个 setTimeout() 方法返回的整数值,将比第一个的整数值大 1
      • function f() {}
        setTimeout(f, 1000); //
        setTimeout(f, 1000); //
        setTimeout(f, 1000); //
      • 利用这一点,可以写一个函数,取消当前所有的 setTimeout() 定时器
        • (function() {
          var gid = setInterval(clearAllTimeouts, 0); function clearAllTimeouts() {
          var id = setTimeout(function() {}, 0);
          while (id > 0) {
          if (id !== gid) {
          clearTimeout(id);
          }
          id--;
          }
          }
          })();

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

  • debounce()

    • 用于消抖的函数
    • 有时,我们不希望回调函数被频繁调用

      • 比如,用户填入网页输入框的内容,希望通过 Ajax 方法传回服务器,jQuery 的写法如下

        $('textarea').on('keydown', ajaxAction);

        这样写有一个很大的缺点,就是如果用户连续击键,就会连续触发keydown事件,造成大量的 Ajax 通信。

      • 这是不必要的,而且很可能产生性能问题。
      • 正确的做法应该是,
        • 设置一个门槛值,表示两次 Ajax 通信的最小间隔时间。
        • 如果在间隔时间内,发生新的 keydown 事件,则不触发 Ajax 通信,并且重新开始计时。
        • 如果过了指定时间,没有发生新的 keydown 事件,再将数据发送出去
        • 这种做法叫做 debounce(防抖动)
          // 假定两次 Ajax 通信的间隔不得小于2500毫秒,上面的代码可以改写成下面这样
          
          $('textarea').on('keydown', debounce(ajaxAction, 2500));
          
          function debounce(fn, delay){
          var timer = null; // 声明计时器
          return function() {
          var context = this;
          var args = arguments;
          clearTimeout(timer);
          timer = setTimeout(function () {
          fn.apply(context, args);
          }, delay);
          };
          }

          只要在2500毫秒之内,用户再次击键,就会取消上一次的定时器,然后再新建一个定时器。这样就保证了回调函数之间的调用间隔,至少是2500毫秒

  • setTimeout() 和 setInterval() 的运行机制

    • 是将指定的代码移出本轮事件循环,等到下一轮事件循环,再检查是否到了指定时间。
    • 如果到了,就执行对应的代码;
    • 如果不到,就继续等待
    • 这意味着,setTimeout() 和 setInterval() 指定的回调函数,必须等到本轮事件循环的所有同步任务都执行完,才会开始执行。

      • 由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,setTimeout() 和 setInterval() 指定的任务,一定会按照预定时间执行。

        setTimeout(someTask, 100);
        veryLongTask(); // 上面代码的setTimeout,指定100毫秒以后运行一个任务。
        // 但是,如果后面的veryLongTask函数(同步任务)运行时间非常长,过了100毫秒还无法结束,那么被推迟运行的someTask就只有等着,等到veryLongTask运行结束,才轮到它执行
    • 再看一个setInterval的例子
      • setInterval(function () {
        console.log(2);
        }, 1000); sleep(3000); function sleep(ms) {
        var start = Date.now();
        while ((Date.now() - start) < ms) {
        }
        }
        // 上面代码中,setInterval要求每隔1000毫秒,就输出一个2。
        // 但是,紧接着的sleep语句需要3000毫秒才能完成,那么setInterval就必须推迟到3000毫秒之后才开始生效。
        // 注意,生效后setInterval不会产生累积效应,即不会一下子输出三个2,而是只会输出一个2。
  • setTimeout(f, 0)
    • 如果指定时间为0,即setTimeout(f, 0),那么会立刻执行吗?

      • 答案是不会。因为上一节说过,必须要等到当前脚本的同步任务,全部处理完以后,才会执行setTimeout指定的回调函数f。
      • 也就是说,setTimeout(f, 0) 会在下一轮事件循环一开始就执行
        setTimeout(function () {
        console.log(1);
        }, 0);
        console.log(2);
        //
        //

        上面代码先输出2,再输出1。因为2是同步任务,在本轮事件循环执行,而1是下一轮事件循环执行

    • setTimeout(f, 0)这种写法的目的

      • 是,尽可能早地执行 f(),
      • 但是并不能保证立刻就执行f()
      • 不同的浏览器有不同的实现
        • 以 Edge 浏览器为例,会等到4毫秒之后运行。
        • 如果电脑正在使用电池供电,会等到16毫秒之后运行;
        • 如果网页不在当前 Tab 页,会推迟到1000毫秒(1秒)之后运行。
        • 这样是为了节省系统资源
      • 应用:

        • 可以调整事件的发生顺序

          • 比如,网页开发中,某个事件先发生在子元素,然后冒泡到父元素,即子元素的事件回调函数,会早于父元素的事件回调函数触发。
          • 如果,想让父元素的事件回调函数先发生,就要用到 setTimeout(f, 0)
            // HTML 代码如下
            // <input type="button" id="myButton" value="click"> var input = document.getElementById('myButton'); input.onclick = function A() {
            setTimeout(function B() {
            input.value +=' input';
            }, 0)
            }; document.body.onclick = function C() {
            input.value += ' body'
            };
          • 上面代码在点击按钮后,先触发回调函数 A ,然后触发函数 C 。

          • 函数 A 中,setTimeout() 将函数 B() 推迟到下一轮事件循环执行,这样就起到了,先触发父元素的 回调函数 C() 的目的了

              • 用户自定义的回调函数,通常在浏览器的默认动作之前触发

                • 比如,用户在输入框输入文本,keypress 事件 会在浏览器接收文本之前触发。因此,下面的回调函数是达不到目的的

                  // HTML 代码如下
                  // <input type="text" id="input-box"> document.getElementById('input-box').onkeypress = function (event) {
                  this.value = this.value.toUpperCase();
                  }
                  // 上面代码想在用户每次输入文本后,立即将字符转为大写。
                  // 但是实际上,它只能将本次输入前的字符转为大写,
                  // 因为浏览器此时还没接收到新的文本,所以 this.value 取不到最新输入的那个字符
                • 解决:
                  document.getElementById('input-box').onkeypress = function() {
                  var self = this;
                  setTimeout(function() {
                  self.value = self.value.toUpperCase();
                  }, 0);
                  }

                  上面代码将代码放入setTimeout之中,就能使得它在浏览器接收到文本之后触发

          • 由于setTimeout(f, 0)实际上意味着,将任务放到浏览器最早可得的空闲时段执行,
          • 所以那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f, 0)里面执行
            • var div = document.getElementsByTagName('div')[0];
              
              // 改变一个网页元素的背景色 写法一    会造成浏览器“堵塞”    因为 JavaScript 执行速度远高于 DOM,会造成大量 DOM 操作“堆积”
              for (var i = 0xA00000; i < 0xFFFFFF; i++) {
              div.style.backgroundColor = '#' + i.toString(16);
              } // 改变一个网页元素的背景色 写法二 setTimeout(f, 0)的好处,解决了上面的问题
              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);
              • 代码高亮的处理

                • 如果代码块很大,一次性处理,可能会对性能造成很大的压力,
                • 那么将其分成一个个小块,一次处理一块,
                • 比如写成 setTimeout(highlightNext, 50) 的样子,性能压力就会减轻

(83)Wangdao.com第十七天_JavaScript 定时器的更多相关文章

  1. (60)Wangdao.com第十天_JavaScript 函数_作用域_闭包_IIFE_回调函数_eval

    函数        实现特定功能的 n 条语句封装体. 1. 创建一个函数对象 var myFunc = new Function(); // typeof myFunc 将会打印 function ...

  2. (50)Wangdao.com第七天_JavaScript 发展与简介

    一个完整的JavaScript 应该由以下三部分组成: ECMAScript DOM,全称Browser Object Model,即浏览器对象模型,主要处理浏览器窗口和框架 BOM,全称Docume ...

  3. (51)Wangdao.com第七天_JavaScript 编写位置及输出语句

    JavaScript 编写位置 编写在html内部标签的属性中 不推荐使用,因为结构和行为耦合,不便于维护 主要有  <button onclick="alert('点我干哈!');& ...

  4. (59)Wangdao.com第十天_JavaScript 对象在 栈和堆

    对象的属性值 如果要使用特殊的属性名,需 对象["属性名"] = 属性值       // 存 对象["属性名"]       // 取 obj["1 ...

  5. (80)Wangdao.com第十六天_JavaScript Object 对象的相关方法

    Object 对象的相关方法 Object.getPrototypeOf() 返回参数对象的原型. 这是获取某对象的原型对象的标准方法. var F = function () {}; var f = ...

  6. (75)Wangdao.com第十三天_JavaScript 包装对象

    包装对象 所谓“包装对象”,就是分别与数值.字符串.布尔值相对应的Number.String.Boolean三个原生对象 这三个原生对象可以把原始类型的值变成(包装成)对象 var v1 = new ...

  7. (53)Wangdao.com第七天_JavaScript 强制类型转换

    其他进制的数字 二进制    以 0b 开头的 Number 八进制    以 0 开头的 Number 十六进制    以 0x 开头的 Number 注意:无论是多少进制的数字,输出都是十进制 强 ...

  8. (54)Wangdao.com第七天_JavaScript 运算符

    JavaScript 运算符,也叫操作符 对一个或者多个值进行运算,都是会返回结果的. 比如: typeof    返回一个值的类型,返回值类型是字符串. 隐式类型转换: 任意值 = 任意值 + &q ...

  9. (62)Wangdao.com第十天_JavaScript 变量的作用域

    在 js 中有两种作用域:全局作用域,局部作用域. 全局作用域 直接写在 <script> 标签中的变量和方法. 在网页打开时创建,在网页关闭时销毁. 全局作用域有一个全局对象 windo ...

随机推荐

  1. Typora使用说明(记录总结)

    目录 区域元素 YAML FONT Matters 菜单 段落 标题 引注 序列 可选序列 代码块 数学块 表格 脚注 水平线 特征元素 链接 超链接 内链接 相关链 URLs 图片 斜体 加粗 删除 ...

  2. react实战项目开发(2) react几个重要概念以及JSX语法

    前言 前面我们已经学习了利用官方脚手架搭建一套可以应用在生产环境下的React开发环境.那么今天这篇文章主要先了解几个react重要的概念,以及讲解本文的重要知识JSX语法 React重要概念 [思想 ...

  3. Quartz C#使用

    参考:https://www.cnblogs.com/lazyInsects/p/8075487.htmlQuartz是一款比较好用的定时任务执行工具类,虽然我们平时也可以自己写代码实现定时执行,但是 ...

  4. jpa/hibernate @onetomany 使用left join 添加多条件,可以使用过滤器filters (with-clause not allowed on fetched associations; use filters异常信息)

    package com.ipinyou.mip.dataAsset.campaignManagement.entity; import com.ipinyou.mip.utils.NumberUtil ...

  5. 清除Windows访问共享时保存的凭据记录

    场景:某些时候我们连接了某台PC或服务器的共享目录或打印机,但因为一些原因突然连接不上了(或是对方组策略发生变化,或是对方计算机用户密码变更 等等..) 又或者电脑给其它用户使用,因一些安全问题需要临 ...

  6. java(7)数组

    一.什么是数组及其作用? 定义:具有相同数据类型的一个集合 作用:存储连续的具有相同类型的数据 二.java中如何声明和定义数组 2.1 声明和定义的语法: 数据类型[ ] 数组名:( int[ ] ...

  7. python开发遇到的坑(2)mongodb安装路径权限问题

    个人比较懒,Mac 电脑直接使用命令行安装,brew install mongodb,但是遇到两个问题,其一: Error: The following directories are not wri ...

  8. poj 2155 matrix 二维线段树 线段树套线段树

    题意 一个$n*n$矩阵,初始全为0,每次翻转一个子矩阵,然后单点查找 题解 任意一种能维护二维平面的数据结构都可以 我这里写的是二维线段树,因为四分树的写法复杂度可能会退化,因此考虑用树套树实现二维 ...

  9. Lua中的表达式

    [算术操作符] Lua支持常规的算术操作符有:”+”(加法),”-“(减法),”*”(乘法),”/”(除法),”^”(指数),”%”(取模),一元的”-“(负号).所有的这些操作符都用于实数.例如:x ...

  10. Lua中的模块与包

    [前言] 从Lua5.1版本开始,就对模块和包添加了新的支持,可是使用require和module来定义和使用模块和包.require用于使用模块,module用于创建模块.简单的说,一个模块就是一个 ...