https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals
-
setTimeout
- 在指定的时间后执行一段代码 -
setInterval
- 以固定的时间间隔,重复运行一段代码 -
requestAnimationFrame
-setInterval()
的现代版本;在浏览器下一次重新绘制显示之前执行指定的代码块,从而允许动画在适当的帧率下运行,而不管它在什么环境中运行.
这些函数设置的异步代码实际上在主线程上运行(在其指定的计时器过去之后)。
setTimeout
setTimeout的语法格式如下:
var timeoutID = scope.setTimeout(function[, delay, arg1, arg2, ...]);
var timeoutID = scope.setTimeout(function[, delay]);
var timeoutID = scope.setTimeout(code[, delay]);
function
可以是匿名函数,也可以直接传递函数名。code
是使用字符串的方式传递函数,这种方式不推荐,原因和eval
一样,是不安全的。arg
是传递给function
的参数
const timeoutId = setTimeout(() => {
console.log(123)
}, 3000)
console.log(timeoutId)
function log(msg) {
console.log(`log: ${msg}`)
}
setTimeout(log, 3000, '打印日志')
clearTimeout
可以在超时前清除掉定时器。在之后清除是没有任何意义的,也就是说setTimeout
是不需要clearTimeout
的,除非是特意想取消。
setInterval
setInterval的语法格式如下:
var intervalID = scope.setInterval(func, delay, [arg1, arg2, ...]);
var intervalID = scope.setInterval(code, delay);
setInterval
必须使用clearInterval
清除,不然会一直执行下去,导致内存泄露。
关于 setTimeout() 和 setInterval() 需要注意的几点
- 使用递归调用
setTimeout
来替代setInterval
为什么有时候用 setTimeout 替代 setInterval?
不要再用 setInterval 做轮询了!
setInterval
有一个缺陷,就是当你的代码有可能比你分配的时间间隔,花费更长时间运行。前一个任务没有执行完,然后又开始执行后一个任务,会导致一些异常问题。对于这种情况,可以使用递归调用setTimeout
来避免这个问题。
let i = 1
setTimeout(function run() {
console.log(i)
i++
if (i == 10) {
return
}
setTimeout(run, 1000)
}, 1000)
- 关于最小延时>=4ms 的问题
在浏览器中,setTimeout()/setInterval()
的每调用一次定时器的最小间隔是4ms
,这通常是由于函数嵌套导致(嵌套层级达到一定深度),或者是由于已经执行的setInterval
的回调函数阻塞导致的。很多人会把这个跟立即超时搞混,需要注意。
- 立即超时
setTimeout(function() {
alert('World');
}, 0);
alert('Hello');
立即超时它不会立即执行。因为任何异步代码仅在主线程可用后才执行(换句话说,当调用栈为空时)。这个涉及到了 JS 事件循环的机制,在事件循环中专门再讲解。
使用 setTimeout 实现一个轮询器
/**
* 使用setTimeout替代setInterval实现的轮询功能
*/
export default class Polling {
constructor() {
this.func = null
//是否停止定时器
this.stopFlag = false
}
/**
* 使用setTimeout轮询
* @param func
* @param ms
*/
start(func, ms) {
if (this.func === null) {
this.func = func
}
// 确保一个 Timer 实例只能重复一个 func
if (this.func !== func) {
return
}
if (!this.stopFlag) {
setTimeout(()=>{
func()
this.start(func, ms)
}, ms)
}
}
/**
* 停止轮询
*/
stop() {
this.stopFlag = true
this.func = null
}
}
使用方式:
const polling = new Polling()
const func = ()=>{
//检查Dom是否存在
const divTask = $("#task_" + data.task_id);
if (!divTask.length) {
return;
}
//Dom存在,结束继续poll
polling.stop()
//执行操作
//......
}
polling.start(func, 100)