BOM系列第一篇之定时器setTimeout和setInterval


Posted in Javascript onAugust 17, 2016

setTimeout()

setTimeout()方法用来指定某个函数或字符串在指定的毫秒数之后执行。它返回一个整数,表示定时器的编号,这个值可以传递给clearTimeout()用于取消这个函数的执行

以下代码中,控制台先输出0,大概过1000ms即1s后,输出定时器setTimeout()方法的返回值1

var Timer = setTimeout(function(){
console.log(Timer);
},1000);
console.log(0);

也可以写成字符串参数的形式,由于这种形式会造成javascript引擎两次解析,降低性能,故不建议使用

var Timer = setTimeout('console.log(Timer);',1000);
console.log(0);

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

以下代码中,控制台出现0和1,但是0却在前面,后面会解释这个疑问

BOM系列第一篇之定时器setTimeout和setInterval

var Timer = setTimeout(function(){
console.log(Timer);
});
console.log(0);

实际上,除了前两个参数,setTimeout()方法还允许添加更多的参数,它们将被传入定时器中的函数中

以下代码中,控制台大概过1000ms即1s后,输出2,而IE9-浏览器只允许setTimeout有两个参数,不支持更多的参数,会在控制台输出NaN

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

可以使用IIFE传参来兼容IE9-浏览器的函数传参

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

或者将函数写在定时器外面,然后函数在定时器中的匿名函数中带参数调用

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

this指向

在this机制系列已经详细介绍过this指向的4种绑定规则,由于定时器中的this存在隐式丢失的情况,且极易出错,因此在这里再次进行说明

var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a : 2,
foo:foo
}
setTimeout(obj.foo,100);//0
//等价于
var a = 0;
setTimeout(function foo(){
console.log(this.a);
},100);//0

若想获得obj对象中的a属性值,可以将obj.foo函数放置在定时器中的匿名函数中进行隐式绑定

var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a : 2,
foo:foo
}
setTimeout(function(){
obj.foo();
},100);//2

或者也可以使用bind方法将foo()方法的this绑定到obj上

var a = 0;
function foo(){
console.log(this.a);
};
var obj = {
a : 2,
foo:foo
}
setTimeout(obj.foo.bind(obj),100);//2

clearTimeout()

setTimeout函数返回一个表示计数器编号的整数值,将该整数传入clearTimeout函数,取消对应的定时器

//过100ms后,控制台输出setTimeout()方法的返回值1
var Timer = setTimeout(function(){
console.log(Timer);
},100);

于是可以利用这个值来取消对应的定时器

var Timer = setTimeout(function(){
console.log(Timer);
},100);
clearTimeout(Timer);

或者直接使用返回值作为参数

var Timer = setTimeout(function(){
console.log(Timer);
},100);
clearTimeout(1);

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

//控制台输出1、2、3
var Timer1 = setTimeout(function(){
console.log(Timer1);
},100);
var Timer2 = setTimeout(function(){
console.log(Timer2);
},100);
var Timer3 = setTimeout(function(){
console.log(Timer3);
},100);

setInterval()

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

<button id="btn">0</button>
<script>
var timer = setInterval(function(){
btn.innerHTML = Number(btn.innerHTML) + 1;
},1000);
btn.onclick = function(){
clearInterval(timer);
btn.innerHTML = 0;
}
</script>

[注意]HTML5标准规定,setTimeout的最短时间间隔是4毫秒;setInterval的最短间隔时间是10毫秒,也就是说,小于10毫秒的时间间隔会被调整到10毫秒

大多数电脑显示器的刷新频率是60HZ,大概相当于每秒钟重绘60次。因此,最平滑的动画效的最佳循环间隔是1000ms/60,约等于16.6ms

为了节电,对于那些不处于当前窗口的页面,浏览器会将时间间隔扩大到1000毫秒。另外,如果笔记本电脑处于电池供电状态,Chrome和IE 9以上的版本,会将时间间隔切换到系统定时器,大约是16.6毫秒

运行机制

下面来解释前面部分遗留的疑问,为什么下面代码的控制台结果中,0出现在1的前面呢?

setTimeout(function(){
console.log(1);
});
console.log(0);

实际上,把setTimeout的第二个参数设置为0s,并不是立即执行函数的意思,只是把函数放入代码队列

在下面这个例子中,给一个按钮btn设置了一个事件处理程序。事件处理程序设置了一个250ms后调用的定时器。点击该按钮后,首先将onclick事件处理程序加入队列。该程序执行后才设置定时器,再有250ms后,指定的代码才被添加到队列中等待执行

btn.onclick = function(){
setTimeout(function(){
console.log(1);
},250);
}

如果上面代码中的onclick事件处理程序执行了300ms,那么定时器的代码至少要在定时器设置之后的300ms后才会被执行。队列中所有的代码都要等到JavaScript进程空闲之后才能执行,而不管它们是如何添加到队列中的

BOM系列第一篇之定时器setTimeout和setInterval

如图所示,尽管在255ms处添加了定时器代码,但这时候还不能执行,因为onclick事件处理程序仍在运行。定时器代码最早能执行的时机是在300ms处,即onclick事件处理程序结束之后

setInterval的问题

使用setInterval()的问题在于,定时器代码可能在代码再次被添加到队列之前还没有完成执行,结果导致定时器代码连续运行好几次,而之间没有任何停顿。而javascript引擎对这个问题的解决是:当使用setInterval()时,仅当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中。这确保了定时器代码加入到队列中的最小时间间隔为指定间隔

但是,这样会导致两个问题:1、某些间隔被跳过;2、多个定时器的代码执行之间的间隔可能比预期的小

假设,某个onclick事件处理程序使用serInterval()设置了200ms间隔的定时器。如果事件处理程序花了300ms多一点时间完成,同时定时器代码也花了差不多的时间,就会同时出现跳过某间隔的情况

BOM系列第一篇之定时器setTimeout和setInterval

例子中的第一个定时器是在205ms处添加到队列中的,但是直到过了300ms处才能执行。当执行这个定时器代码时,在405ms处又给队列添加了另一个副本。在下一个间隔,即605ms处,第一个定时器代码仍在运行,同时在队列中已经有了一个定时器代码的实例。结果是,在这个时间点上的定时器代码不会被添加到队列中

迭代setTimeout

为了避免setInterval()定时器的问题,可以使用链式setTimeout()调用

setTimeout(function fn(){
setTimeout(fn,interval);
},interval);

这个模式链式调用了setTimeout(),每次函数执行的时候都会创建一个新的定时器。第二个setTimeout()调用当前执行的函数,并为其设置另外一个定时器。这样做的好处是,在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。而且,它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行

使用setInterval()

<div id="myDiv" style="height: 100px;width: 100px;background-color: pink;position:absolute;left:0;"></div>
<script>
myDiv.onclick = function(){
var timer = setInterval(function(){
if(parseInt(myDiv.style.left) > 200){
clearInterval(timer);
return false;
}
myDiv.style.left = parseInt(myDiv.style.left) + 5 + 'px'; 
},16); 
}
</script>

  使用链式setTimeout()

<div id="myDiv" style="height: 100px;width: 100px;background-color: pink;position:absolute;left:0;"></div>
<script>
myDiv.onclick = function(){
setTimeout(function fn(){
if(parseInt(myDiv.style.left) <= 200){
setTimeout(fn,16); 
}else{
return false;
}
myDiv.style.left = parseInt(myDiv.style.left) + 5 + 'px'; 
},16); 
}
</script>

应用

使用定时器来调整事件发生顺序

【1】网页开发中,某个事件先发生在子元素,然后冒泡到父元素,即子元素的事件回调函数,会早于父元素的事件回调函数触发。如果,我们先让父元素的事件回调函数先发生,就要用到setTimeout(f, 0)

正常情况下,点击div元素,先弹出0,再弹出1

<div id="myDiv" style="height: 100px;width: 100px;background-color: pink;"></div>
<script>
myDiv.onclick = function(){
alert(0);
}
document.onclick = function(){
alert(1);
}
</script>

如果进行想让document的onclick事件先发生,即点击div元素,先弹出1,再弹出0。则进行如下设置

<div id="myDiv" style="height: 100px;width: 100px;background-color: pink;"></div>
<script>
myDiv.onclick = function(){
setTimeout(function(){
alert(0);
})
}
document.onclick = function(){
alert(1);
}
</script>

【2】用户自定义的回调函数,通常在浏览器的默认动作之前触发。比如,用户在输入框输入文本,keypress事件会在浏览器接收文本之前触发。因此,下面的回调函数是达不到目的

<input type="text" id="myInput">
<script>
myInput.onkeypress = function(event) {
this.value = this.value.toUpperCase();
}
</script>

上面代码想在用户输入文本后,立即将字符转为大写。但是实际上,它只能将上一个字符转为大写,因为浏览器此时还没接收到文本,所以this.value取不到最新输入的那个字符

只有用setTimeout改写,上面的代码才能发挥作用

<input type="text" id="myInput">
<script>
myInput.onkeypress = function(event) {
setTimeout(function(){
myInput.value = myInput.value.toUpperCase();
});
}
</script>

代码到此结束。下篇给大家介绍

以上所述是小编给大家介绍的BOM系列第一篇之定时器setTimeout和setInterval ,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
javascript 装载iframe子页面,自适应高度
Mar 20 Javascript
JavaScript 对话框和状态栏使用说明
Oct 25 Javascript
js 火狐下取本地路径实现思路
Apr 02 Javascript
左侧是表头的JS表格控件(自写,网上没有的)
Jun 04 Javascript
jquery购物车结算功能实现方法
Oct 29 Javascript
正则中的回溯定义与用法分析【JS与java实现】
Dec 27 Javascript
基于Vue实现支持按周切换的日历
Sep 24 Javascript
JavaScript实现的原生态兼容IE6可调可控滚动文字功能详解
Sep 19 Javascript
vue中vee validate表单校验的几种基本使用
Jun 25 Javascript
vuex如何重置所有state(可定制)
Jan 17 Javascript
使用React手写一个对话框或模态框的方法示例
Apr 25 Javascript
Vue路由守卫及页面登录权限控制的设置方法(两种)
Mar 31 Javascript
BOM系列第二篇之定时器requestAnimationFrame
Aug 17 #Javascript
AngularJS 视图详解及示例代码
Aug 17 #Javascript
BOM系列第三篇之定时器应用(时钟、倒计时、秒表和闹钟)
Aug 17 #Javascript
AngularJS Ajax详解及示例代码
Aug 17 #Javascript
AngularJS包括详解及示例代码
Aug 17 #Javascript
详细分析Javascript中创建对象的四种方式
Aug 17 #Javascript
AngularJS表单详解及示例代码
Aug 17 #Javascript
You might like
PHP daddslashes 使用方法介绍
2012/10/26 PHP
php实现文件下载实例分享
2014/06/02 PHP
php筛选不存在的图片资源
2015/04/28 PHP
php及codeigniter使用session-cookie的方法(详解)
2017/04/06 PHP
javascritp实现input输入框相关限制用法
2007/06/29 Javascript
JavaScript DOM 添加事件
2009/02/14 Javascript
使用GruntJS构建Web程序之安装篇
2014/06/04 Javascript
jquery左边浮动到一定位置时显示返回顶部按钮
2014/06/05 Javascript
JQuery datepicker 用法详解
2015/12/25 Javascript
基于JavaScript实现瀑布流效果(循环渐近)
2016/01/27 Javascript
Angular 2.0+ 的数据绑定的实现示例
2017/08/09 Javascript
详解Vue双向数据绑定原理解析
2017/09/11 Javascript
基于vue.js的分页插件详解
2017/11/27 Javascript
Angular实现搜索框及价格上下限功能
2018/01/19 Javascript
Vue使用vux-ui自定义表单验证遇到的问题及解决方法
2018/05/10 Javascript
深入了解js原型模式
2019/05/30 Javascript
JS中数组实现代码(倒序遍历数组,数组连接字符串)
2019/12/29 Javascript
[36:41]完美世界DOTA2联赛循环赛FTD vs Magma第一场 10月30日
2020/10/31 DOTA
浅谈python迭代器
2017/11/08 Python
python中的字典操作及字典函数
2018/01/03 Python
python:print格式化输出到文件的实例
2018/05/14 Python
解决python flask中config配置管理的问题
2019/07/26 Python
python二分法查找算法实现方法【递归与非递归】
2019/12/06 Python
Jupyter Notebook的连接密码 token查询方式
2020/04/21 Python
解决python调用自己文件函数/执行函数找不到包问题
2020/06/01 Python
css3 flex实现div内容水平垂直居中的几种方法
2020/03/27 HTML / CSS
最耐用行李箱,一箱永流传:Briggs & Riley(全球终身保修)
2017/12/07 全球购物
迪卡侬中国官网:Decathlon中国
2020/08/10 全球购物
德国的大型美妆个护电商:Flaconi
2020/06/26 全球购物
一名女生的自荐信
2013/12/08 职场文书
生产部统计员岗位职责
2014/01/05 职场文书
护士辞职信模板
2014/01/20 职场文书
2014年图书馆个人工作总结
2014/12/18 职场文书
忠犬八公的故事观后感
2015/06/05 职场文书
贫困证明怎么写
2015/06/16 职场文书
HDFS免重启挂载新磁盘
2022/04/06 Servers