文章目录
  1. 1. JS 的计时器
    1. 1.1. 超时调用 setTimeout()
    2. 1.2. 间歇调用 setInterval()
    3. 1.3. 区别
  2. 2. JS 计时器的原理
  3. 3. 参考

JS 的计时器

JS 是单线程语言,但它允许通过设置 “超时值” 和 “间歇时间值” 来调度代码在特定的时刻执行。

  1. 超时调用:window.setTimeout() 在指定的时间过后执行代码
  2. 间歇调用:window.setInterval() 每隔指定的时间间隔重复执行代码,直到间歇调用被取消or页面被卸载

它两都是 window 对象的方法,所以 this 的值…需要注意哦~

JavaScript 的核心是 ECMAScript,而在 Web 中使用 JS,真正的核心无疑是 BOM(浏览器对象模型)。BOM 的核心是 window,它表示浏览器的一个实例。在浏览器中,window 对象扮演以下两种角色:
1.通过 JS 访问浏览器窗口的一个接口
2.ECMAScript 规定的 Global 对象:在网页中定义的任何一个对象、变量、函数,都以 window 作为其全局对象,也就有权访问 parseInt()、setTimeout()、setInterval() 等方法了

超时调用 setTimeout()

以下代码会在1秒后显示一个警告框。

1
2
3
setTimeout(function(){
alert('Hello world');
}, 1000);

第一个参数:要执行的代码
可以是字符串,比如”alert(‘Hello world’)”,就和 eval() 函数里使用的字符串一样(但传递字符串有性能损失,故不建议用)
也可以是函数(建议用 function,即如上)

第二个参数:表示等待多长时间的毫秒数,即经过多长时间把当前任务添加到任务队列中去。
如果队列是空,那添加的代码就会立即执行;如果队列不为空,那就等等前面的代码执行完了之后才能执行。所以说,这里设置的该事件后指定的代码不一定会执行,只能更长,不会更短。

返回值:一个数值 ID,表示超时调用,这个 ID 是计划执行代码的唯一标识符
可以通过它来取消调用,clearTimeout(timeoutId); 只要是在指定的时间尚未执行之前取消调用,那就可以完全取消超时调用。【那如果是已经在任务队列里排队待执行嘞?记得 jQuery 动画的 stop() 方法就有专门的参数问是否要清空任务队列里的。Try下~】

间歇调用 setInterval()

间歇调用与超时调用类似,参数与之相同;间歇调用ID;取消间隔调用 clearInterval(intervalId);

1
2
3
var intervalId = setInterval(function(){
alert('Hello world');
}, 1000);

区别

1
2
3
4
5
6
7
8
setTimeout(function() {
// do something
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
// do something
}, 10);

以上这段代码看起来功能是等价的,但事实却不是的。
setTimeout 的代码会在每次回调函数执行之后至少需要延时10ms才执行(只可能更多,但不会少)。但是 setInterval 会每隔 10ms 就去尝试执行一次回调函数,而不管上一个回调函数是不是还在执行。它并不关心当前谁在执行,它的回调函数会不加区分地进入队列。
执行异步代码时,setTimeout 和 setInterval 是有本质区别的。如果 interval 回调函数执行需要很长时间(比指定的延时长),那 interval 有可能会没有延时地一个接一个地被执行。

最好不用间歇调用,原因:
1.取消调用(非循环一直调用时):
(1)取消间隔调用的重要性要远远高于取消超时调用,因为在不加干涉的情况下,间歇调用将会一直执行到页面卸载。
(2)在使用超时调用时,没必要跟踪超时调用ID。因为每次执行代码后,如果不另外设置,调用会自行停止。
2.一般认为,使用超时调用来模拟间歇调用是一种最佳模式。因为后一个间歇调用可能会在前一个间歇调用结束之前启动,而超时调用则可以完全避免这一点。所以在开发环境下,很少使用真正的间歇调用。

JS 计时器的原理

由于 JS 是一个单线程的解释器,所以,它一定时间内只能执行一段代码。它有一个 JS 的任务队列来专门控制要执行的代码。这些任务会按照将它们添加到队列的顺序来执行(据说排队和调用的方式会依据浏览器的不同而不同,此处简化为直接排在最后-且一一调用)
所以,事实上,设置的计时器的延时是没有保证的。所有在浏览器中执行的 JS 单线程异步事件(比如计时器、鼠标点击事件、XMLHttpRequest请求完成)都只有在它有空的时候才会执行。

看下这张图,源自JavaScript的计时器的工作原理

垂直方向是以毫秒计的时间;蓝色的块代表了当前正在执行的 JS 代码段。
把上图翻译成代码模拟下,代码摘自JavaScript的计时器的工作原理,并对其进行了些修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn">点击我</button>
<script src="jquery-1.11.2.js"></script>
<script>
var startTime = new Date();
// 初始化超时调用
var timer = setTimeout(function(){
var end = new Date();
console.log('10ms的超时调用完成,距程序开始 ' + (end-startTime) + 'ms');
}, 10);
// 鼠标点击事件
var asyncStart;
var btn = $('#btn');
btn.click(function(){
var asyncEnd = new Date();
console.log('模拟鼠标执行时间完成,花费时间 ' + (asyncEnd-asyncStart) + 'ms');
});
// 初始化间歇调用
var intervalStart = new Date();
var count = 1;
var interval = setInterval(function(){
count++;
if(count==5){
clearInterval(interval);
}
var intervalEnd = new Date();
console.log('间歇调用,与上次执行间隔 ' + (intervalEnd-intervalStart) + 'ms');
intervalStart = intervalEnd;
}, 10);
// 模拟第一阶段代码执行
var start = new Date();
console.log('第一阶段代码执行开始...');
var first = [];
for(var i=0; i<10000000; i++){
first.push(i);
// 模拟鼠标点击事件:在第一阶段代码执行期间
if(i==1000000){
asyncStart = new Date();
console.log('鼠标点击事件开始,距程序开始 ' + (asyncStart-startTime) + 'ms');
btn.trigger('click');
}
}
var end = new Date();
console.log('第一阶段代码执行完成,用时 ' + (end-start) + 'ms');
</script>
</body>
</html>

计时器在哪初始化,就在哪开始计时。在 Chrome 中的运行结果如下:

按照上面的原理来解释下运行结果:

  1. 一开始设定的超时调用并不是在10ms后立即执行,而是被添加到了任务队列的后面。等待第一阶段执行完成后才执行的,距离初始化时的时间也不是10ms
  2. 鼠标点击事件是在第一阶段代码执行期间被触发的,它没有等待第一阶段执行完毕,而是被高优执行了(在Chrome中测试。或许是浏览器自己做的优化吧,以便及时响应用户的交互)
  3. 最后执行间歇调用,它也不是严格按照10ms的间隔执行的。

参考

JavaScript的计时器的工作原理: http://segmentfault.com/a/1190000002633108
《JavaScript高级程序设计(第3版)》 中的第8章: 8.1.6 间歇调用和超时调用

文章目录
  1. 1. JS 的计时器
    1. 1.1. 超时调用 setTimeout()
    2. 1.2. 间歇调用 setInterval()
    3. 1.3. 区别
  2. 2. JS 计时器的原理
  3. 3. 参考