前言:由面试题引发的学习笔记,语言的学习应该是细腻的,而不是一味地热捧主流框架,有很多地方框架并不能帮你多少,比如响应式交互这块,打算学完几大框架之后细细地琢磨JavaScript高阶设计,开始每天整理一些学习笔记,日后遇到相同的问题起码先有相关的概念,没办法,who let me so vagetable


“在开发环境下,很少使用间歇调用(setInterval),原因是后一个间歇调用很可能在前一个间歇调用结束前启动”

————《JavaScript高级程序设计》

由于js执行机制setInterval的固有弊端

JS执行遇到setInterval便会间歇性将函数体插入到异步队列,不管先插入的函数体是否执行完,就开始计时,比如间歇时间是500ms,那么不管那时候前一个方法是否已经执行完毕,都会把后一个方法放入执行的序列中。这时候就会发生一个问题,假如前一个方法的执行时间超过500ms,加入是1000ms,那么就意味着,前一个方法执行结束后,后一个方法马上就会执行,因为此时间歇时间已经超过500ms了。简单来说导致两个问题:

  • 某些间隔会被跳过

  • 多个定时器的代码执行时间可能会比预期小

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
var executeTimes = 0;
var intervalTime = 500;
var intervalId = null;
var oriTime = new Date().getTime();

function intervalFun(){
executeTimes++;
var nowExecuteTimes = executeTimes;
var timeDiff = new Date().getTime() - oriTime;
console.log("doIntervalFun——"+nowExecuteTimes+", after " + timeDiff + "ms");
var delayParam = 0;
sleep(1000);
console.log("doIntervalFun——"+nowExecuteTimes+" finish !");
if(executeTimes==5){
clearInterval(intervalId);
}
}

function timeOutFun(){
executeTimes++;
var nowExecuteTimes = executeTimes;
var timeDiff = new Date().getTime() - oriTime;
console.log("doTimeOutFun——"+nowExecuteTimes+", after " + timeDiff + "ms");
var delayParam = 0;
sleep(1000);
console.log("doTimeOutFun——"+nowExecuteTimes+" finish !");
if(executeTimes<5){
setTimeout(arguments.callee,intervalTime);
}
}
//sleep是一个普通的'睡觉'函数,sleep(1000)表示运行该函数会花费1秒
function sleep(sleepTime){
var start=new Date().getTime();
while(true){
if(new Date().getTime()-start>sleepTime){
break;
}
}
}


// intervalId = setInterval(intervalFun,intervalTime);

setTimeout(timeOutFun,intervalTime);

效果实现

分别运行上面最后两行代码:

setInterval

执行setInterval

执行setInterval

可以发现,fun2和fun1开始的间歇接近1000ms,刚好就是fun1的执行时间,也就意味着fun1执行完后fun2马上就执行了,和我们间歇调用的初衷背道而驰

setTimeout

执行setTimeout

执行setTimeout

fun1和fun2相差了1500ms = 1000 + 500,fun2在fun1执行完的500ms后执行,setTimeout在内部实现了递归调用配合硬性判断条件来触发,因为这里的sleep函数是同步的,代码均为依次执行