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 相关文章推荐
jquery DIV撑大让滚动条滚到最底部代码
Jun 06 Javascript
JS的Document属性和方法小结
Sep 17 Javascript
javascript框架设计读书笔记之字符串的扩展和修复
Dec 02 Javascript
angular.js指令中的controller、compile与link函数的不同之处
May 10 Javascript
vue如何从接口请求数据
Jun 22 Javascript
JS中定位 position 的使用实例代码
Aug 06 Javascript
jquery鼠标悬停导航下划线滑出效果
Sep 29 jQuery
jQuery实现验证表单密码一致性及正则表达式验证邮箱、手机号的方法
Dec 05 jQuery
Vue resource三种请求格式和万能测试地址
Sep 26 Javascript
JS实现点击按钮随机生成可拖动的不同颜色块示例
Jan 30 Javascript
react-router-dom 嵌套路由的实现
May 02 Javascript
深入解析微信小程序开发中遇到的几个小问题
Jul 11 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
用来解析.htgroup文件的PHP类
2012/09/05 PHP
PHP+MYSQL中文乱码问题
2015/07/01 PHP
PHP调用存储过程返回值不一致问题的解决方法分析
2016/04/26 PHP
Yii框架分页实现方法详解
2017/05/20 PHP
PHP Post获取不到非表单数据的问题解决办法
2018/02/27 PHP
如何判断图片地址是否失效
2007/02/02 Javascript
javascript 写类方式之五
2009/07/05 Javascript
用JS实现一个TreeMenu效果分享
2011/08/28 Javascript
IE下使用cloneNode注意事项分享
2012/11/22 Javascript
window.returnValue使用方法示例介绍
2014/07/03 Javascript
jquery动态添加删除(tr/td)
2015/02/09 Javascript
JavaScript比较当前时间是否在指定时间段内的方法
2016/08/02 Javascript
jQuery+HTML5+CSS3制作支持响应式布局时间轴插件
2016/08/10 Javascript
详解javascript表单的Ajax提交插件的使用
2016/12/29 Javascript
用jQuery旋转插件jqueryrotate制作转盘抽奖
2017/02/10 Javascript
js判断手机系统是android还是ios
2017/03/07 Javascript
详解vue模拟加载更多功能(数据追加)
2017/06/23 Javascript
推荐VSCode 上特别好用的 Vue 插件之vetur
2017/09/14 Javascript
图片懒加载imgLazyLoading.js使用详解
2020/09/15 Javascript
Angular使用操作事件指令ng-click传多个参数示例
2018/03/27 Javascript
解决Mac node版本升级失败的问题
2018/05/16 Javascript
详解json串反转义(消除反斜杠)
2019/08/12 Javascript
axios实现文件上传并获取进度
2020/03/25 Javascript
jQuery操作选中select下拉框的值代码实例
2020/02/07 jQuery
python3.3教程之模拟百度登陆代码分享
2014/01/16 Python
对Python中数组的几种使用方法总结
2018/06/28 Python
pyqt5对用qt designer设计的窗体实现弹出子窗口的示例
2019/06/19 Python
图片上传插件ImgUploadJS:用HTML5 File API 实现截图粘贴上传、拖拽上传
2016/01/20 HTML / CSS
软件测试英文面试题
2012/10/14 面试题
一道输出判断型Java面试题
2014/10/01 面试题
中学生在校期间的自我评价分享
2013/11/13 职场文书
2014世界杯球队球队口号
2014/06/05 职场文书
爱国主义教育基地观后感
2015/06/18 职场文书
关于html选择框创建占位符的问题
2021/06/09 HTML / CSS
Axios取消重复请求的方法实例详解
2021/06/15 Javascript
SQL 尚未定义空闲 CPU 条件 - OnIdle 作业计划将不起任何作用
2021/06/30 SQL Server