setTimeout和setInterval基本用法:
- 指定延迟时间后调用函数
- 以指定周期调用函数
使用setInterVal:
function doStuff(){
// 此处为需要执行一段时间T的代码
}
setInterVal(doStuff, 100);
复制代码
下来看如何使用setTimeout模拟setInterval:
function tick() {
doStuff();
setTimeout(tick, 100);
}
tick();
复制代码
看下正常情况下两者的区别:
setInterval每个定时器之间的间隔是100ms,而setTimeout每隔100ms执行一次doStuff,所以每个定时器之间的间隔是100 + T(doStuff执行时间为T);这个T就是本文的关键了。
- 如果T可以忽略的话,两者的效果是基本相同的。
- T <= 100时,setInterval定时器间隔100,setTimeout定时器间隔100+T。
- 如果T > 100,setTimeout依然如上图,两个定时器之间间隔100+T。 那么setInterval呢?
先看下图:
在0ms时,定时器1开始进入宏任务队列;100ms时,定时器1开始执行doStuff1,队列为空,定时器2进入队列;200ms时,因为定时器2还在队列中,所以定时器3被跳过。浏览器不会同时创建两个相同的间隔计时器。 300ms时,定时器2已经开始执行,队列为空,定时器4进入队列。以此类推~
下面我们用代码验证下。T设置为140ms。 我们让定时器运行5次,按照上述理解,总运行时间应该是:100+5*140 = 800ms。 代码如下:
let i = 0;
console.time("总时间");
function doStuff() {
console.log("delay");
dead(140);
console.timeEnd("测试");
}
function dead(delay) {
var start = new Date().getTime();
while (new Date().getTime() < start + delay);
}
let timer = setInterval(() => {
i++;
if (i > 4) {
clearInterval(timer);
setTimeout(() => {
console.timeEnd("总时间");
}, 0);
}
console.log("interval start");
console.time("测试");
doStuff();
}, 100);
复制代码
运行结果:
可以看出定时器运行了5次,总时间的确为800ms。 如果doStuff中的代码是异步的呢?比如像我们常用的请求接口。 简单起了个本地服务器,140ms返回结果。代码很简单就不上了。下面是js代码:
let i = 0;
console.time("总时间");
function delay() {
fetchData();
}
function fetchData() {
return fetch("/home", {
method: "POST"
})
.then(res => {
console.log("res>>>>>>");
})
}
let timer = setInterval(() => {
i++;
if (i > 4) {
clearInterval(timer);
setTimeout(() => {
console.timeEnd("总时间");
}, 0);
}
delay();
}, 100);
复制代码
控制台输出:
可以看出总时间是500ms,请求接口的异步代码并不会阻塞定时器。这个也很好理解,定时器中的同步代码会直接进入队列,异步代码注册事件,完成后进入队列;所以当异步代码注册事件后,这个定时器就执行完了,并不是等异步代码回来后这个定时器才算结束,不然5次定时器的总时间就是800ms了。结论:
- setInterval只是在特定时间点将代码推入队列,如果已有定时器在队列中,则会跳过。浏览器不会同时创建两个相同的间隔计时器。
- setInterval中的异步代码不会阻塞创建新的定时器。
参考文章: