1. settimeout用法
1.1 基本使用
语法: setTimeout(code,millisec)
code必填,调用函数;millisec非必填,如果不填就认为是0。settimeout()表示millisec毫秒后把code的代码放到执行队列(注意不是立即执行,后面会讲到)
例子:
let timeout2 = setTimeout(() => {
console.log('haha')
}, 1000);
1000ms后会输出‘haha’
作为定时器使用
let test = function(){
console.log('wait a moment', i)
i++
timeout3 = setTimeout(() => {
test()
}, 3000);
if(i === 10){
clearTimeout(timeout3)
}
}
test()
上面这段程序会定时输出打印的话:wait a moment0,1,2…
1.2 有关执行队列
以前一直认为写了setTimeout(fn, millisec)这段代码之后,fn就一定会再millisec后执行,其实这是不对的,看下面的例子:
console.log('now time:', Date.parse(new Date()))
setTimeout(() => {
console.log('execute after 10s, now time', Date.parse(new Date()))
let now = Date.parse(new Date())
while(Date.parse(new Date()) - now < 10000){}
}, 10000);
setTimeout(() => {
console.log('execute after 10s? now time:', Date.parse(new Date()))
}, 10000);
这段代码执行结果:
now time: 1644246966000
execute after 10s, now time 1644246976000
execute after 10s? now time: 1644246986000
可以发现后面的setTimeout()并没有按照预期在10000ms之后执行,而是等待第一个setTimeou()执行之后再执行
这也就解释了setTimeout()定义的millisec并不是执行时间,而是放到执行队列的时间,有关执行队列不明白的可以看这篇文章
2. setTimeout执行顺序
2.1 基础
首先需要明白的是js会执行完同步代码,然后才执行执行队列的顺序,如果执行队列只有setTimeout,执行顺序是按照放入队列的顺序执行的:millisec小的先执行,如果millisec相同,按照代码写的顺序执行
console.log('now time:', Date.parse(new Date()))
setTimeout(() => {
console.log('execute after 10s, now time', Date.parse(new Date()))
let now = Date.parse(new Date())
// while(Date.parse(new Date()) - now < 10000){}
}, 10000);
setTimeout(() => {
console.log('execute after 10s? now time:', Date.parse(new Date()))
}, 10000);
打印结果:
now time: 1644247542000
execute after 10s, now time 1644247552000
execute after 10s? now time: 1644247552000
上面的例子把阻塞程序注释掉,millsec相同,会先执行前面先写的setTimeout,注意打印的时候now time看起来是一样的,大概是因为时间间隔太近,约等于相同时间,=
2.2 经典例子
for(var i = 0; i<3;i++){
console.log('i max 3 no brackets', i)
setTimeout(() => {
console.log('i max 3 with brackets',i)
}, 1000);
}
for(var i=0; i<10;i++){
console.log('i max 10 no brackets', i)
setTimeout(() => {
console.log('i max 10 with brackets', i)
}, 0);
}
执行结果:
i max 3 no brackets 0
i max 3 no brackets 1
i max 3 no brackets 2
i max 10 no brackets 0
i max 10 no brackets 1
i max 10 no brackets 2
i max 10 no brackets 3
i max 10 no brackets 4
i max 10 no brackets 5
i max 10 no brackets 6
i max 10 no brackets 7
i max 10 no brackets 8
i max 10 no brackets 9
i max 10 with brackets 10
i max 10 with brackets 10
i max 10 with brackets 10
i max 10 with brackets 10
i max 10 with brackets 10
i max 10 with brackets 10
i max 10 with brackets 10
i max 10 with brackets 10
i max 10 with brackets 10
i max 10 with brackets 10
i max 3 with brackets 10
i max 3 with brackets 10
i max 3 with brackets 10
这个例子验证了前面的说法,执行顺序:
同步代码>setTimeout的millisec小的>setTimeout的millsec大的
另外关注到这里的i的输出,会发现setTimeout输出的都是10,这是因为使用了var定义变量,循环的i是同一个,等到执行setTimout的时候i已经变成了10,如果想要让setTimeout输出每次循环进入的时候的i的值,可以通过let解决,let认为花括号就是一个作用块,而var是es5中的语法,只认为function是一个作用域,不认识花括号,具体的解释可以参考我的这篇文章
3. 和promise的执行顺序
先说结论:
同步代码和promise里面的同步代码是等价的 > then里面的代码 > 外部的settimeout setTimeout中同样的millisec,执行顺序依据第二个参数排序,如果第二个参数相同,那就按照代码编写顺序排序
看例子:
setTimeout(() => {
console.log('外部settimeout执行第一次')
});
new Promise((resolve)=>{
console.log('马上执行for循环啦')
setTimeout(() => {
console.log('promise里面同步代码执行settimeout')
});
resolve()
}).then(() =>{
console.log('进入then循环')
})
console.log('循环结束啦')
setTimeout(() => {
console.log('外部settimeout执行第二次')
});
输出结果:
马上执行for循环啦
循环结束啦
进入then循环
外部settimeout执行第一次
promise里面同步代码执行settimeout
外部settimeout执行第二次
4. 总结
1、setTimeout(fn, millisec)表示的是millisec之后把fn放入执行队列,具体执行时间根据执行队列之前的执行情况决定,不一定是millisec之后就会执行
2、执行顺序:
同步代码和promise里面的同步代码是等价的 > then里面的代码 > 外部的settimeout setTimeout中同样的millisec,执行顺序依据第二个参数排序,如果第二个参数相同,那就按照代码编写顺序排序