小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
基本使用
setTimeout/setInterval
语法
let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)
参数说明:
-
func|code:要执行的函数或代码字符串
-
delay:执行前的延时,以毫秒为单位(1000 毫秒 = 1 秒),默认值是 0
-
arg1
,arg2
…:要传入被执行函数(或代码字符串)的参数列表(IE9 以下不支持)
示例:
普通用法
带参数
注意:
传入函数的时候,不要加括号();
因为setTimeout
期望得到一个对函数的引用。而 test()
是在执行函数,所以实际上传给 setTimeout
的是 函数的执行结果
clearTimeout/clearInterval
用来取消setTimeout,setTimeout调用的时候会返回一个“定时器标识符”,所以取消执行这样写
let timerId = setTimeout(...);
clearTimeout(timerId);
let timerId = setInterval(...);
clearInterval(timerId)
综合示例
// 每 1 秒重复一次
let timerId = setInterval(() => console.log('执行'), 1000);
// 3 秒之后停止
setTimeout(() => { clearInterval(timerId); console.log('停止'); }, 3000);
setTimeout的零延时用法
使用方法
setTimeout(func, 0)或setTimeout(func)
这样可以让 func
尽快执行。但是只有在当前正在执行的脚本执行完成后,程序才会调用它。也就是说,该函数被调度在当前脚本执行完成“之后”立即执行。
示例:
下面的代码,先输出了Hello
,然后立刻输出World
第一行代码,我们使用的setTimeout的0延时,但是它实际上是等下面的打印执行完之后才执行的。所以先输出 "Hello"
,然后才输出 "World"
。
垃圾回收和回调
当一个函数传入 setInterval/setTimeout
时,将为其创建一个内部引用,并保存在调度程序中。这样,即使这个函数没有其他引用,也能防止垃圾回收器(GC)将其回收。
- 对于
setInterval
,传入的函数会一直存在于内存中,直到clearInterval
被调用。
所以:如果函数引用了外部变量(译注:闭包),那么只要这个函数还存在,外部变量也会随之存在。它们可能比函数本身占用更多的内存。因此,当我们不再需要调度函数时,最好取消它,即使这是个(占用内存)很小的函数。
setInterval和setTimeout的区别
setTimeout
函数推迟到一段时间间隔之后再执行
- 【只执行一次】
setInterval
允许我们重复运行一个函数,从一段时间间隔之后开始运行,之后以该时间间隔连续重复运行该函数。
- 【每间隔给定的时间周期性执行】
简单的说:setTimeout执行一次就结束了,setInterval如果没有被清除,会一直循环执行下去。
setInterval存在的问题
setInterval
这个虽然能设置隔一段时间后不断执行,但是实际上只是将事件放入消息队列,真正执行的时间并不确定,所以就可能出很多种不同的情况。
我们用以下示例来说明,以下的setInterval,每100毫秒调用一次func函数,那么可能出现
【情况1】:func函数执行的时间 < 100毫秒
即在遇到下一个100毫秒前就执行完了。
【情况2】:func函数执行的时间 > 100毫秒
当下一次的func函数已经触发了,上一次的都含没有执行完;这个时候,func会在队列(这里的队列是指event loop)中等待,直到上一次的函数执行完
【情况3】:func的函数执行事件特别长,执行过程中触发了好多个func。猜猜这个时候是什么个情况?大家一个个乖乖排好队等着执行吗?
实时上并不是这样,只要发现队列中有一个被执行的函数存在,那么其他的统统会被忽略。如下图,在第300毫秒和400毫秒处的回调都被抛弃,一旦第一个函数执行完后,接着执行队列中的第二个,即使这个函数已经“过时”很久了。
虽然在setInterval的里指定的周期是100毫秒,但它并不能保证两个函数之间调用的间隔一定是一百毫秒。在上面的情况中,如果队列中的第二个函数时在第450毫秒处结束的话,在第500毫秒时,它会继续执行下一轮func,也就是说这之间的间隔只有50毫秒,而非周期100毫秒。(如果函数每次执行时间都超过 delay
设置的时间,那么每次调用之间将完全没有停顿。)
从上面的几个情况中,可以看到setInterval
存在这么几个问题
- 使用 setInterval 时,某些间隔会被跳过;
- 可能多个定时器会连续执行;
用setTimeout来代替setInterval
let timerId = setTimeout(function run() {
console.log('执行');
timerId = setTimeout(run, 1000);
}, 1000);
上面这个 setTimeout
在当前这一次函数执行完时立即执行下一次调用。
嵌套的 setTimeout
要比 setInterval
灵活得多。采用这种方式可以根据当前执行结果来执行下一次调用,因此下一次调用可以与当前这一次不同。
参考:
Scheduling: setTimeout and setInterval
🎨【点赞】【关注】不迷路,更多前端干货等你解锁
往期推荐
今天的文章JavaScript中的setTimeout 和 setInterval分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18923.html