最近在开发小程序中,经常用到setTimeout和setInterval函数,曾经经常搞混这两个函数的使用,特意研究了一下这里做下总结,希望同行看了能有所收获,不再迷路。
首先,要说明的是setTimeout和setInterval是两个实现了定时调用的函数,而不是类似thread的线程。线程一般会在一个时间片内, 可以并发的执行调用,但这两个函数并不是这样使用的,因为我们都知道JavaScript都是以单线程的方式运行于浏览器的JavaScript引擎中的。
setTimeout和setInterval的作用只是把你要执行的代码在你设定的一个时间点插入JavaScript引擎维护的一个代码队列中, 插入代码队列并不意味着你的代码就会立马执行的,理解这一点很重要。
一、setTimeout
setTimeout() 只执行 code 一次。如果要多次调用,请使用 setInterval() 或者让 code 自身再次调用 setTimeout()。
funcName:function() {
// dosomething1
setTimeout(() => {
// dosomething2
}, 500);
// dosomething3
}
以上代码的执行顺序是:先执行dosomething1的内容, 然后运行到setTimeout的地方, setTimeout会告诉浏览器说: "500ms后会插一段要执行的代码给你的队列中", 浏览器答应了(注意插入代码并不意味着立马执行), setTimeout代码运行后, 紧跟其后的dosomething3代码开始执行。
问题来了,如果dosomething3的代码执行时间超过500ms, 那结果会是如何? 500ms一到, dosomething2代码会立马执行吗?事实可能会让你有点失望, 在dosomething3执行过程中(执行了500ms后)dosomething2代码被插入代码队列, 但一直要等funcName的方法执行结束, 才会执行dosomething2代码段, 从代码队列上看dosomething2代码是在funcName后面的, 再加上js以单线程方式执行, 所以就很容易理解了。 如果是另一种情况, dosomething3代码执行的时间
用完定时器之后,要记得清除:clearTimeout(timeoutId) 这里的timeoutId是setTimeout返回的一个正整数编号,是定时器的唯一标识符。
二、setInterval
setInterval可以当成setTimeout的升级版,就像setTimeout循环调用自身,用法也跟setTimeout一样,用完时也要记得用clearInterval清掉定时器。
这里可能会存在两个问题:
1.时间间隔或许会跳过
2.时间间隔可能小于定时调用的代码的执行时间
click:function() {
// dosomething1
setInterval(() => {
// dosomething2
}, 500);
// dosomething3
}
同样当funcName开始执行时,当dosomething1代码执行完后执行setInterval, 以此为一个时间点, 在200ms后插入dosomething2代码, funcName代码顺利结束, dosomething2代码开始执行, 如果dosomething2代码也执行了一个比较长的时间, 超过了接下来一个插入时间点400ms, 这样代码队列后又插入了一份dosomething2代码, dosomething2继续执行着, 而且超过了600ms这个插入时间点, 然后问题就来了, 开始以为代码队列后面会继续插入一份dosomething2代码...而实际情况是,由于代码队列中已经有了一份未执行的dosomething2代码, 所以600ms这个插入时间点将会被"无情"的跳过, 因为JavaScript引擎只允许有一份未执行的dosomething2代码,惊不惊喜意不意外?
所以有一种更好的方式来实现此功能,主要思路是递归:
setTimeout(() => {
//processing
setTimeout(arguments.callee, interval);
}, interval);
这里有个知识点:
arguments 的主要用途是保存函数参数, 有个callee属性,返回正被执行的Function对象,也就是所指定的Function对象的正文,这有利于匿名函数的递归或者保证函数的封装性。
但是现在已经不推荐使用arguments.callee();
因为访问 arguments 是个很昂贵的操作,因为它是个很大的对象,每次递归调用时都需要重新创建,影响现代浏览器的性能,还会影响闭包。
三、setTimeout和setInterval区别
setInterval在执行完一次代码之后,经过了给定的时间间隔,它还会自动重复执行代码,而setTimeout只执行一次那段代码。
setInterval和setTimeout都返回定时器对象标识符,用于clearInterval和clearTimeout调用
clearTimeout(showTimes) //清除已设置的setTimeout对象
clearInterval(showTimes) //清除已设置的setInterval对象
现在是不是觉得很简单,很容易区分?那么下面两个例子就很容易区分差别了。
例一:
setTimeout(function(){
console.log("小马");
setTimeout(function(){arguments.callee;},1000);
},1000)
例二:
setInterval(function(){
console.log("小马");
},1000);