介绍
setTimeout
它就是 ⼀个定时器,⽤来指定某个函数在多少毫秒之后执⾏
它会返回⼀个整数,表⽰定时器的编号,同时你还可 以通过该编号来取消这个定时器。
示例:
function showName(){
console.log("极客时间")
}
var timerID = setTimeout(showName,200);
浏览器怎么实现setTimeout
我们知道渲染进程中所有运⾏在主线程 上的任务都需要先添加到消息队列,我们知道渲染进程中所有运⾏在主线程 上的任务都需要先添加到消息队列,例如:
- 当接收到HTML⽂档数据,渲染引擎就会将“解析DOM”事件添加到消息队列中
- 当⽤⼾改变了Web⻚⾯的窗⼝⼤⼩,渲染引擎就会将“重新布局”的事件添加到消息队列中。
- 当触发了JavaScript引擎垃圾回收机制,渲染引擎会将“垃圾回收”任务添加到消息队列中。
- 同样,如果要执⾏⼀段异步JavaScript代码,也是需要将执⾏任务添加到消息队列中。
在Chrome中除了正常使⽤的消息队列之外,还有另外⼀个消息队列,这个队列中维护了需要延迟执⾏的任务列表,包括了定时器和Chromium内部⼀些需要延迟执⾏的任务。所以当通过JavaScript创建⼀个定时器 时,渲染进程会将该定时器的回调任务添加到延迟队列中。
当通过JavaScript调⽤setTimeout设置回调函数的时候,渲染进程将会创建⼀个回调任务,包含了回调函 数showName、当前发起时间、延迟执⾏时间
创建好回调任务之后,再将该任务添加到延迟执⾏队列中
浏览器会根据发起时间和延迟时间计算出到期的任务,然后依次 执⾏这些到期的任务
使⽤setTimeout的⼀些注意事项
如果当前任务执⾏时间过久,会影延迟到期定时器任务的执⾏
在使⽤setTimeout的时候,有很多因素会导致回调函数执⾏⽐设定的预期值要久,其中⼀个就是当前任务 执⾏时间过久从⽽导致定时器设置的任务被延后执⾏
function bar() {
console.log('bar')
}
function foo() {
setTimeout(bar, 0);
for (let i = 0; i < 5000; i++) {
let i = 5+8+8+8
console.log(i)
}
}
foo()
在执⾏foo函数的时候使⽤setTimeout设置了⼀个0延时的回调任务,设置好回调任务后,foo 函数会继续执⾏5000次for循环。
通过setTimeout设置的回调任务被放⼊了消息队列中并且等待下⼀次执⾏,这⾥并不是⽴即执⾏的;要执 ⾏消息队列中的下个任务,需要等待当前的任务执⾏完成,由于当前这段代码要执⾏5000次的for循环,所 以当前这个任务的执⾏时间会⽐较久⼀点。这势必会影响到下个任务的执⾏时间。
如果setTimeout存在嵌套调⽤,那么系统会设置最短时间间隔为4毫秒
也就是说在定时器函数⾥⾯嵌套调⽤定时器,也会延⻓定时器的执⾏时间
function cb() {
setTimeout(cb, 0); }
setTimeout(cb, 0);
在Chrome中,定时 器被嵌套调⽤5次以上,系统会判断该函数⽅法被阻塞了,如果定时器的调⽤时间间隔⼩于4毫秒,那么浏览 器会将每次调⽤的时间间隔设置为4毫秒。
未激活的⻚⾯,setTimeout执⾏最⼩间隔是1000毫秒
未被激活的⻚⾯中定时器最⼩值⼤于1000毫 秒,也就是说,如如果标签不是当前的激活标签,那么定时器最⼩的时间间隔是1000毫秒,⽬的是为了优化 后台⻚⾯的加载损耗以及降低耗电量。
延时执⾏时间有最⼤值
Chrome、 Safari、Firefox都是以32个bit来存储延时值的,32bit最⼤只能存放的数字是2147483647毫秒,这就意味 着,如果setTimeout设置的延迟值⼤于 2147483647毫秒(⼤约24.8天)时就会溢出,这导致定时器会被⽴ 即执⾏。
function showName(){
console.log("极客时间")
}
var timerID = setTimeout(showName,2147483648);//会被理解调⽤执⾏
这段代码是会⽴即被执⾏的
使⽤setTimeout设置的回调函数中的this不符合直觉
如果被setTimeout推迟执⾏的回调函数是某个对象的⽅法,那么该⽅法中的this关键字将指向全局环境,⽽ 不是定义时所在的那个对象。
var name= 1;
var MyObj = {
name: 2,
showName: function(){
console.log(this.name);
}
}
setTimeout(MyObj.showName,1000)
这⾥输出的是1,因为这段代码在编译的时候,执⾏上下⽂中的this会被设置为全局window
解决方法
第⼀种是将MyObj.showName放在匿名函数中执⾏
//箭头函数
setTimeout(() => {
MyObj.showName()
}, 1000);
//或者function函数
setTimeout(function() {
MyObj.showName();
}, 1000
第⼆种是使⽤bind⽅法,将showName绑定在MyObj上⾯
setTimeout(MyObj.showName.bind(MyObj), 1000)