setTimeout 与 setInterval
setTimeout
可以起到延迟一定时间后,再执行函数,以及实现异步效果。 除此外,setInterval
可以进行重复的执行函数,相当于嵌套调用 setTimeout
。
这两个日常开发经常遇到,但是它的参数以及一些细节却很多。
由于 setTimeout
和 setInterval
绝大部分都有类似的语法特性,本文主要以 setTimeout
为主。特殊的地方进行单独标注。
用法
setTimeout
的语法:
var timeoutID = scope.setTimeout(function[, delay, arg1, arg2, ...]);
var timeoutID = scope.setTimeout(function[, delay]);
var timeoutID = scope.setTimeout(code[, delay]);
第一,支持传递给执行函数参数方式调用(但是不兼容IE9等旧浏览器):
const param1 = 'a'
const param2 = 'b'
const param3 = 'c'
setTimeout((a, b, c) => {
console.log(a, b, c)
}, 1000, param1, param2, param3)
// 1s 后打印出 a b c
这种方式我从来没用过,因为可以直接通过外层获取参数。
const param1 = 'a'
const param2 = 'b'
const param3 = 'c'
setTimeout(() => {
console.log(param1, param2, param3)
}, 1000)
// 1s 后打印出 a b c
这么做好处是更加简单,缺点我认为是外层变量不能被垃圾回收。
第一,支持不写延迟时间方式(相当于写 0
):
setTimeout(() => {
console.log('a')
})
// 立即打印出 a
第三,支持直接写代码方式(而不是函数):
const param1 = 'a'
setTimeout('console.log(param1)', 1000)
// 1s 后打印出 a
这种方式理论上先执行了 eval
,也就继承了 eval
的缺点,而且我从来没想到项目中有什么场景要用到。
场景
最常见的用法就是延迟执行,比如:
- 中间页,几秒 后跳转到另一个页面;
- 弹窗关闭是有动画的,所以调用关闭方法后,再等待一小会,执行清理弹窗的 DOM 结构;
- 轮询,间隔多少秒就请求接口查询状态。
还有就是强行改异步作用,比如如下代码:
const myFn = (callback) => {
try {
// some code ...
callback()
setTimeout(callback)
} catch (e) {
console.log('inner catch', e)
}
}
myFn(() => {
window.abc()
})
// 错误 inner catch TypeError: window.abc is not a function
我写了一个公共方法,自己捕捉错误(inner catch
)。调用方调用,结果外层代码有 bug,反而被我捕获到了。
此时强行改成异步,可以防止自己捕获错误信息。
const myFn = (callback) => {
try {
// some code ...
setTimeout(callback) // 改成异步
} catch (e) {
console.log('inner catch', e)
}
}
myFn(() => {
window.abc()
})
// 错误 Uncaught TypeError: window.abc is not a function