1.介绍
JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout() 和setInterval() 这两个函数来完成。
它们向任务队列添加定时任务
2.setTimeout
setTimeout 函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。
var timerId = setTimeout(func|code, delay);
上面代码中,setTimeout 函数接受两个参数,第一个参数func|code 是将要推迟执行的函数名或者一段代码,第二个参数delay 是推迟执行的毫秒数。
console.log(1);
setTimeout('console.log(2)',1000); //这里不论设置多久, 哪怕是0 , 也是后执行 console.lo(2)
console.log(3);
// 1
// 3
// 2
上面代码会先输出1和3,然后等待1000毫秒再输出2。
注意,console.log(2) 必须以字符串的形式,作为setTimeout 的参数。//目前Chrome已经不支持这种方式, 因为认为不安全
版本 81.0.4044.138(正式版本) (64 位)
如果推迟执行的是函数,就直接将函数名,作为setTimeout 的参数。//推荐写法
function f() {
console.log(2);
}
setTimeout(f, 1000);
setTimeout 的第二个参数如果省略,则默认为0。
setTimeout(f)
// 等同于
setTimeout(f, 0)
除了前两个参数,setTimeout 还允许更多的参数。它们将依次传入推迟执行的函数(回调函数)。
setTimeout(function (a,b) {
console.log(a + b);
}, 1000, 1, 1);
输出结果: 3
上面代码中,setTimeout 共有4个参数。最后那两个参数,将在1000毫秒之后回调函数执行时,作为回调函数的参数。
还有一个需要注意的地方,
如果回调函数是对象的方法,那么setTimeout 使得方法内部的this 关键字指向全局环境,而不是定义时所在的那个对象。
var x = 1;
var obj = {
x: 2,
y: function () {
console.log(this.x);
}
};
setTimeout(obj.y, 1000) // 1
上面代码输出的是1,而不是2。
因为当obj.y 在1000毫秒后运行时,this 所指向的已经不是obj 了,而是全局环境。
为了防止出现这个问题,一种解决方法是将obj.y 放入一个函数。
var x = 1;
var obj = {
x: 2,
y: function () {
console.log(this.x);
}
};
setTimeout(function () {
obj.y();
}, 1000);
// 2
上面代码中,obj.y 放在一个匿名函数之中,这使得obj.y 在obj 的作用域执行,而不是在全局作用域内执行,所以能够显示正确的值。
另一种解决方法是,使用bind 方法,将obj.y 这个方法绑定在obj 上面。
var x = 1;
var obj = {
x: 2,
y: function () {
console.log(this.x);
}
};
setTimeout(obj.y.bind(obj), 1000)
// 2
3.setInterval
setInterval 函数的用法与setTimeout 完全一致,区别仅仅在于setInterval 指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
var i = 1
var timer = setInterval(function() {
console.log(2);
}, 1000)
上面代码中,每隔1000毫秒就输出一个2,会无限运行下去,直到关闭当前窗口。
与setTimeout 一样,除了前两个参数,setInterval 方法还可以接受更多的参数,它们会传入回调函数。
下面是一个通过setInterval 方法实现网页动画的例子。
var div = document.getElementById('someDiv');
var opacity = 1;
var fader = setInterval(function() {
opacity -= 0.1;
if (opacity >= 0) {
div.style.opacity = opacity;
} else {
clearInterval(fader);
}
}, 100);
上面代码每隔100毫秒,设置一次div 元素的透明度,直至其完全透明为止。
setInterval 的一个常见用途是实现轮询。下面是一个轮询 URL 的 Hash 值是否发生变化的例子。
var hash = window.location.hash;
var hashWatcher = setInterval(function() {
if (window.location.hash != hash) {
updatePage();
}
}, 1000);
Note:
setInterval 指定的是“开始执行”之间的间隔,并不考虑每次任务执行本身所消耗的时间。
因此实际上,两次执行之间的间隔会小于指定的时间。
比如,setInterval 指定每 100ms 执行一次,每次执行需要 5ms,那么第一次执行结束后95毫秒,第二次执行就会开始。
如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。
为了确保两次执行之间有固定的间隔,可以不用setInterval ,而是每次执行结束后,使用setTimeout 指定下一次执行的具体时间。
var i = 1;
var timer = setTimeout(function f() {
// ...
timer = setTimeout(f, 2000);
}, 2000);
上面代码可以确保,下一次执行总是在本次执行结束之后的2000毫秒开始。
Note: 上面的code即替换setInterval
4.clearTimeout(),clearInterval()
setTimeout 和setInterval 函数,都返回一个整数值,表示计数器编号。
var id1 = setTimeout(f, 1000);
var id2 = setInterval(f, 1000);
clearTimeout(id1);
clearInterval(id2);
上面代码中,回调函数f 不会再执行了,因为两个定时器都被取消了。
setTimeout 和setInterval 返回的整数值是连续的,也就是说,第二个setTimeout 方法返回的整数值,将比第一个的整数值大1。
Note: setTimeout 和setInterval 两者之间返回的ID值是在相互基础上交叉+1的
截图:
利用这一点,可以写一个函数,取消当前所有的setTimeout 定时器。//TBD
(function() {
// 每轮事件循环检查一次
var gid = setInterval(clearAllTimeouts, 0);
function clearAllTimeouts() {
var id = setTimeout(function() {}, 0);
while (id > 0) {
if (id !== gid) {
clearTimeout(id);
}
id--;
}
}
})();
上面代码中,先调用setTimeout ,得到一个计算器编号,然后把编号比它小的计数器全部取消。
5.实例:debounce 函数 //防抖动
https://wangdoc.com/javascript/async/timer.html#实例:debounce-函数
code:
<!DOCTYPE html>
<html>
<head>
<title>防抖动</title>
<style type="text/css">
textarea{
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div id="someDiv">hello world</div>
<textarea></textarea>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
(function(){
//console.log(window);
//var dom = $('#someDiv');
//console.log(dom);
//return;
$('textarea').on('keydown', debounce(ajaxAction, 2500)); //防抖动间隔为2.5s 可以将数值改为0 效果更佳明显
function debounce(fn, delay){
var timer = null; // 声明计时器
return function() {
var context = this;
var args = arguments;
// console.log(args)
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
function ajaxAction(para){
console.log(para, 'ajaxAction');
}
})()
</script>
</body>
</html>
6.运行机制
https://wangdoc.com/javascript/async/timer.html#运行机制
7.setTimeout(f, 0) //应用场景有一些, 建议练习一下
https://wangdoc.com/javascript/async/timer.html#settimeoutf-0
code:
<!DOCTYPE html>
<html>
<head>
<title>可以调整事件的发生顺序/用户自定义的回调函数,通常在浏览器的默认动作之前触发</title>
<style type="text/css">
textarea{
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div id="someDiv">hello world</div>
<input type="button" id="myButton" value="click">
<input type="text" id="input-box">
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
(function(){
//1.可以调整事件的发生顺序[这里目标是先执行父级元素的事件,然后执行子元素的事件]
// var input = document.getElementById('myButton');
// input.onclick = function A() {
// setTimeout(function B() {
// input.value +=' input';
// }, 0)
// };
// document.body.onclick = function C() {
// input.value += ' body' //先执行父级元素的事件
// };
//2.用户自定义的回调函数,通常在浏览器的默认动作之前触发 -- 想在用户每次输入文本后,立即将字符转为大写
document.getElementById('input-box').onkeypress = function (event) {
//this.value = this.value.toUpperCase(); //这种写法, 只能让前面输入的字母大写, 最后一个可能还是输入时的样子
//改写如下:
var self = this;
setTimeout(function() {
self.value = self.value.toUpperCase();
}, 0);
}
//3.那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f, 0)里面执行
var div = document.getElementsByTagName('div')[0];
// console.log(div);
// 写法一 //会看到阻塞 浏览器刷新图标一直在打转
// for (var i = 0xFFFFFF; i > 0xA00000; i--) {
// div.style.backgroundColor = '#' + i.toString(16); //可以转换为 rgb(160, 0, 1)形式
// }
// 写法二 //会看到没有阻塞 浏览器刷新图标没有打转, 而且可以看到页面上颜色的渐变效果
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);
//4.另一个使用这种技巧的例子是代码高亮的处理。如果代码块很大,一次性处理,可能会对性能造成很大的压力,那么将其分成一个个小块,一次处理一块,比如写成setTimeout(highlightNext, 50)的样子,性能压力就会减轻。
})()
</script>
</body>
</html>
后续补充
...
|