JavaScript 提供定时执行代码的功能,叫作定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成。它们向任务队列添加定时任务ajax
setTimeout()
setInterval()
clearTimeout(),clearInterval()
实例:debounce 函数
运行机制
setTimeout(f, 0)
含义
应用浏览器
1.setTimeout()
执行多少毫秒后执行,返回一个编号(顺序递增),用于取消。
第一个参数func|code是将要推迟执行的函数名或者一段代码,第二个参数delay是推迟执行的毫秒数。后面的参数为传给回调函数的参数
setTimeout(function (a,b) {
console.log(a + b);
}, 1000, 1, 1);服务器
This(回调函数为对象方法)为全局
var x = 1;app
var obj = {
x: 2,
y: function () {函数
console.log(this.x);
}
};性能
setTimeout(obj.y, 1000) // 1
上面代码输出的是1,而不是2。由于当obj.y在1000毫秒后运行时,this所指向的已经不是obj了,而是全局环境。动画
为了防止出现这个问题,一种解决方法是将obj.y放入一个函数。this
var x = 1;code
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.setInterval()
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);
时间
不考虑执行时间即会小于100ms ,第二次执行就会开始。若是某次执行耗时特别长,好比须要105毫秒,那么它结束后,下一次执行就会当即开始。
为了确保两次执行之间有固定的间隔,能够不用setInterval,而是每次执行结束后,使用setTimeout指定下一次执行的具体时间。
var i = 1;
var timer = setTimeout(function f() {
// ...
timer = setTimeout(f, 2000);
}, 2000);
上面代码能够确保,下一次执行老是在本次执行结束以后的2000毫秒开始。
3.clearTimeout(),clearInterval()
利用这一点,能够写一个函数,取消当前全部的setTimeout定时器。
(function() {
// 每轮事件循环检查一次
var gid = setInterval(clearAllTimeouts, 0);
function clearAllTimeouts() {
var id = setTimeout(function() {}, 0); while (id > 0) { if (id !== gid) { clearTimeout(id); } id--; }
}
})();
上面代码中,先调用setTimeout,获得一个计算器编号,而后把编号比它小的计数器所有取消
4.实例:debounce 函数
debounce(防抖动)监听时间时(Keypress,一直触发函数。
有时,咱们不但愿回调函数被频繁调用。好比,用户填入网页输入框的内容,但愿经过 Ajax 方法传回服务器,jQuery 的写法以下。
$('textarea').on('keydown', ajaxAction);
$('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毫秒。
5.运行机制
将指定的代码移到下一轮事件循环,等这轮轮完,再检查是否到了指定时间。若是到了,就执行对应的代码;若是不到,就继续等待。
回调函数必须等本轮运行完,所以时间不肯定。
setTimeout(someTask, 100);
veryLongTask();
上面代码的setTimeout,指定100毫秒之后运行一个任务。可是,若是后面的veryLongTask函数(同步任务)运行时间很是长,过了100毫秒还没法结束,那么被推迟运行的someTask就只有等着,等到veryLongTask运行结束,才轮到它执行
6.setTimeout(f, 0)
6.1含义
由于上一节说过,必需要等到当前脚本的同步任务,所有处理完之后,才会执行setTimeout指定的回调函数f
setTimeout(function () {
console.log(1);
}, 0);
console.log(2);
// 2
// 1
上面代码先输出2,再输出1。由于2是同步任务,在本轮事件循环执行,而1是下一轮事件循环执行。
setTimeout(f, 0)会在下一轮事件循环一开始就执行
6.2应用
6.2.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的目的了。
6.2.2另外一个应用是,用户自定义的回调函数,一般在浏览器的默认动做以前触发。
好比,用户在输入框输入文本,keypress事件会在浏览器接收文本以前触发。所以,下面的回调函数是达不到目的的。
// HTML 代码以下
// <input type="text" id="input-box">
document.getElementById('input-box').onkeypress = function (event) {
this.value = this.value.toUpperCase();
}
上面代码想在用户每次输入文本后,当即将字符转为大写。可是实际上,它只能将本次输入前的字符转为大写,由于浏览器此时还没接收到新的文本,因此this.value取不到最新输入的那个字符。只有用setTimeout改写,上面的代码才能发挥做用。
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];
// 写法一
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);
上面代码有两种写法,都是改变一个网页元素的背景色。写法一会形成浏览器“堵塞”,由于 JavaScript 执行速度远高于 DOM,会形成大量 DOM 操做“堆积”,而写法二就不会,这就是setTimeout(f, 0)的好处。
6.2.3另外一个使用这种技巧的例子是代码高亮的处理若是代码块很大,一次性处理,可能会对性能形成很大的压力,那么将其分红一个个小块,一次处理一块,好比写成setTimeout(highlightNext, 50)的样子,性能压力就会减轻