JS定时器

JS定时器

javascript提供了定时器来控制代码定时执行,我们把它叫做定时器(timer),主要由settimeout()和setinterval()这两个函数来实现。

setTimeout()

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

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

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

示例1:延迟执行代码

console.log(1);
setTimeout('console.log(2)',1000);
console.log(3);

新建页面,写入js(这里使用phpstudy搭建web服务)
在这里插入图片描述
执行结果:
在这里插入图片描述
为了效果明显,我们将时间延迟设置为了5000毫秒,果然执行结果中2是最后输出的。

示例2:推迟执行一个函数

	console.log(1);
    function f() {
     console.log(2);
    }
    setTimeout(f, 1000);
    console.log(3);

在这里插入图片描述
注:这里将第二个参数省略的话,默认延迟时间为0

示例3:回调函数实现

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

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

上面的示例中将1,1作为第一个函数的参数传入进行回调。
在这里插入图片描述

示例4:关于对象的方法的调用

var x = 1;

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

setTimeout(obj.y, 1000)

执行结果:
在这里插入图片描述
这里的执行结果出人意料的成为了1,显然不符合逻辑。起始是因为在obj.y在1000毫秒后运行时,this已经不在指向obj了,而是全局环境。
解决方案有两个:
方案一:直接将此函数放入一个函数里面执行

var x = 1;

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

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

上面代码中,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)

执行结果:
在这里插入图片描述
注:bind详解—>call、apply、bind 方法详解

setinterval()

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

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

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

示例:实现网页动画

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

<body>
    <div id="someDiv">i am batman!</div>
</body>
<script>
//通过id抓取元素 
var div = document.getElementById('someDiv');
//定义透明度的初始值为1---不透明
var opacity = 1;
//启用setinterval函数进行延时循环
var fader = setInterval(function() {
    //透明度递减
    opacity -= 0.1;
    if (opacity >= 0) {
    div.style.opacity = opacity;
    } else {
    //判断透明度小于0后利用函数clearinterval结束延时循环
    clearInterval(fader);
   }
}, 800);
</script>

其中延时间隔设置的是800毫秒,看的清楚些

关闭定时器

setTimeoutsetInterval函数,都返回一个整数值,表示计数器编号。将该整数传入clearTimeoutclearInterval函数,就可以取消对应的定时器。

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

clearTimeout(id1);
clearInterval(id2);

定时器的应用—防抖函数

有时,我们不希望回调函数被频繁调用。比如,用户填入网页输入框的内容,希望通过 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; // 声明计时器id
  return function() {
    //ajax参数提交
    var context = this;
    var args = arguments;
    //清除计时器---如果有的话
    clearTimeout(timer);
    //为参数提交设定计时器
    timer = setTimeout(function () {
      //延时后执行的ajax动作,这里采用了apply方法保证this的正确指向
      fn.apply(context, args);
    }, delay);
  };
}

实现逻辑,每次用户点击提交时,都会为其创建新的计时器延时提交结果。那么当用户因为某种原因疯狂点击提交时则会一直刷新计时器,阻止参数提交

运行机制

setTimeoutsetInterval的运行机制,是将指定的代码移出本轮事件循环,等到下一轮事件循环,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就继续等待。

这意味着,setTimeoutsetInterval指定的回调函数,必须等到本轮事件循环的所有同步任务都执行完,才会开始执行。由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,setTimeoutsetInterval指定的任务,一定会按照预定时间执行。
例如:

setTimeout(someTask, 100);
veryLongTask();

上面代码的setTimeout,指定100毫秒以后运行一个任务。但是,如果后面的veryLongTask函数(同步任务)运行时间非常长,过了100毫秒还无法结束,那么被推迟运行的someTask就只有等着,等到veryLongTask运行结束,才轮到它执行。

Q:setTimeout的作用是将代码推迟到指定时间执行,如果指定时间为0,即setTimeout(f, 0),那么会立刻执行吗?
答案:不会。因为在先前的运行机制中说过,必须要等到当前脚本的同步任务,全部处理完以后,才会执行setTimeout指定的回调函数f。也就是说,setTimeout(f, 0)会在下一轮事件循环一开始就执行。

setTimeout(f, 0)的一些应用示例

示例1:用户在输入框内输入的数据即时转换为大写
<body>
    <input type="text" id="input-box">
</body>
<script>
    document.getElementById('input-box').onkeypress = function (event) {
    this.value = this.value.toUpperCase();
    }
</script>

在这里插入图片描述
我们很明显的发现输入的字符并不能即时进行大小写转换处理。那么我们采用settimeout(f,0)就可以解决这个问题

document.getElementById('input-box').onkeypress = function() {
  var self = this;
  setTimeout(function() {
    self.value = self.value.toUpperCase();
  }, 0);
}

在这里插入图片描述

示例2:大任务分解执行

由于setTimeout(f, 0)的低优先级执行,实际上意味着,将任务放到浏览器最早可得的空闲时段执行,所以那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f, 0)里面执行。可以有效减轻浏览器的加载压力
例如我们要实现对某一个元素的背景进行快速变换:

<body>
    <div>
        <p>background_test</p>
    </div>
</body>
<script>
   var div = document.getElementsByTagName('div')[0];

    // 写法一---目前测试不可行
        for (var i = 0xA00000; i < 0xFFFFFF; i++) {
        div.style.backgroundColor = '#' + i.toString(16);
        }

    // 写法二
    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);
</script>

上面代码有两种写法,都是改变一个网页元素的背景色。写法一会造成浏览器“堵塞”,因为 JavaScript 执行速度远高于 DOM,会造成大量 DOM 操作“堆积”,而写法二就不会,这就是setTimeout(f, 0)的好处。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值