深入理解setTimeout()和setInterval()函数
概要
在某些场景下可能不需要立刻执行一个函数,而是某个时间片之后执行,这样的过程一般称为“调度执行”。
要实现调度执行,有两种方法可以实现:
setTimeOut -> 指某时间间隔之后执行一次函数
setInterval -> 指安装一定时间间隔有规律执行函数
这些函数不是Javascript规范的一部分,但是大多数环境有内部调度并提供了这些方法。并且所有浏览器和Node.Js都支持。
setTimeout()函数
setTimeout()函数用于在一定时间后执行相关的逻辑,通常接收两个参数: 要执行的回调函数和执行前的等待时间(单位ms)。第一个参数可以是包含javascript代码的字符串或者一个函数,第二个参数是要等待的毫秒数而不是执行代码的确切时间。例如:
// 3秒后显示警告框
setTimeout(() => {
alert("Hello Javascript")
}, 3000)
Javascript是单线程的,所以每次只能执行一段代码,为了调度不同代码的执行,Javascript维护了一个任务队列,其中的任务会按照添加的先后顺序执行。setTimeout()的第二个参数只是告诉Javascript引擎在执行的毫秒数后把任务添加到这个队列。如果队列是空的,则会立即执行,否则,代码段则等待前面的任务执行完才能执行。
调用setTimeout()时,会返回一个表示该超时排期的数值ID,这个超时ID是排期任务的唯一标识符,可用于取消该任务。要取消等待的排期任务,可以调用clearTimeout()方法并传入超时ID,例如:
// 设置超时任务
const timeoutID = setTimeout(() => {
alert("Hello Javascript")
}, 3000)
// 取消超时任务
clearTimeout(timeoutID )
只要是在指定时间到达之前执行clearTimmeout()就可以取消任务,在任务执行之后调用没有效果。
所有超时执行的代码都会在全局作用域中的一个匿名函数中运行,因此函数中的this值在非严格模式下始终指向window,而在严格模式下是undifined,如果给setTimeout提供了一个箭头函数,那么this会保留为定义它时所在的词汇作用域。
setInterval()函数
setInterval()指定的任务会每隔一段时间就执行一次,知道取消循环或者页面卸载,setInterval同样接收两个参数: 一是要执行的代码,二是下一次执行定时代码的任务添加到队列需要等待的时间,例如:
setInterval(() => {
alert("Hello Javascript")
}, 3000)
注意: 需要注意的是,第二个参数间隔时间,指的是向队列添加新任务之前等待的时间。比如,调用setInterval()的时为06:00:00,间隔时间为3000ms,这意味着当06:00:03时,浏览器会把任务添加到执行队列。浏览器不关心这个任务什么时间执行或者执行需要花费多长时间,因此到了06:00:06,它会再向队列添加一个任务,由此可以看出,执行时间短,非阻塞的回调函数比较适合setInterval()
setInterval()方法也会返回一个循环定时ID,可以用于在未来某个时间点取消循环定时,要取消循环定时任务,可以调用clearInterval()并传入定时ID,相对于setTimeout()而言,取消定时任务对setInterval()更加重要,毕竟,如果一直不管它,定时任务就会一直执行到页面卸载
使用setTimeout实现setInterval类似的功能
使用setInterval()实现循环定时任务
let num = 0
let intervalId = null
let max = 10
let incrementNumber = function () {
num ++
if (num === max) {
clearInterval(intervalId)
}
}
intervalId = setInterval(incrementNumber, 300)
这个例子,变量num会每300ms递增一次,直到达到最大值,此时循环定时会被取消。
使用setTimeout()实现同样的功能
let num = 0
let max = 10
let incrementNumber = function () {
num ++
if (num < max) {
setTimeout(incrementNumber, 300)
}
}
setTimeout(incrementNumber, 300)
注意使用setTimeout时不一定要记录超时ID, 以为他会满足条件时自动停止,否则会自动设置另一个超时任务,这个模式是设置循环任务的推荐做法。setInterval()在生产环境很少使用,因为一个任务结束和下一个任务开始之间的时间间隔是无法保证的,有些循环定时任务可能因此而被跳过,而像前面这个例子使用setTimeout()则不会出现这种情况。一般来说,最好不要使用setInterval()。