setInterval 准吗
只能确保回调函数不会再指定的事件间隔之前运行,但可能会在那个时刻运行,也可能在那之后运行,要根据事件队列的状态而定。——《你不知道的 JavaScript》 p143
用 setTimeout 代替 setInterval
- 为什么要替代?
根据事件循环的执行机制,就会知道 setInterval 是等待 call stack 为空的时候,才会执行回调,如果前面的代码执行太久了,超出了给 setInterval 设定的时间间隔,此时回调函数已经进入队列,等 stack 终于为空的时候就会立即执行队列中的回调函数,这时候 web APIs 中的 setInterval 计时也已经在计时了一段时间了,很快又会把回调函数放入队列,就会发现怎么上一次执行完后,没到以为的事件间隔又执行了······· 另外,如果 setInterval 本身给定的回调函数执行的时间比设定的时间间隔长,也会带来这样的问题,失去了设置时间间隔的意义。
- 如何代替?
思路
可以参考这个视频,思路说得很详细JavaScript用setTimeout模拟实现setInterval ,不过视频中没有说如何清除定时器,下面记录下实现清除定时器的思考过程。
设计接口:调用方式和 setInterval 一样 使用:timer = mySetInterval(fn, delay) 清除:myClearInterval(timer)
// 方法一(这种不好想清除定时器,因为不好设置全局的 timer,视频中没有说这个方法)
function mySetInterval(fn, delay) {
setTimeout(() => {
console.log(new Date().toLocaleString()) // 可以打印时间看看
fn()
mySetInterval(fn, delay)
}, delay)
}
// 测试
function testmySetInterval() {
console.log('testmySetInterval')
}
mySetInterval(testmySetInterval, 1000)
方法一不好清除定时器,因为 mySetInterval 肯定要返回一个 timer 才能清除对不对?来看方法二。
// 方法二(视频的方法,没有写怎么清除定时器)
function mySetInterval(fn, delay) {
function inside() {
console.log(new Date().toLocaleString()) // 可以打印时间看看
fn()
setTimeout(inside, delay)
}
setTimeout(inside, delay)
}
// 开始想:
// 假如 mySetInterval 需要返回一个 timer,因为使用方式是 timer = mySetInterval(fn, delay),这样一写 timer 就被固定了对不对?
function mySetInterval(fn, delay) {
let timer = null
function inside() {
clearTimeout(timer)
fn()
timer = setTimeout(inside, delay)
}
timer = setTimeout(inside, delay)
return timer // timer = mySetInterval(fn, delay) 的时候 timer 被固定
}
// mySetInterval 只调用了一次,这样的直接返回的永远都是第一个 setTimeout 的 timer。如何让 timer 不固定呢?对象!返回一个对象 clearTimeout() 作为属性值返回!属性值是个方法,清除定时器,就是让myClearInterval 调用这个方法!这样写:
function mySetInterval(fn, delay) {
let timer = null
function inside() {
console.log(new Date().toLocaleString()) // 打印看看时间
clearTimeout(timer) // 把上一次的 timer 掉,这里使用了闭包, inside 访问了不属于自己作用域的变量,也就是 mySetInterval 下的 timer
fn()
timer = setTimeout(inside, delay)
}
timer = setTimeout(inside, delay)
return { // 返回一个对象 clearTimeout() 作为属性值返回!
clear() {
clearTimeout(timer)
}
}
}
// 清除定时器
function myClearInterval(flagTimer) {
flagTimer.clear()
}
// 测试
function testmySetInterval() {
console.log('testmySetInterval')
}
const timer = mySetInterval(testmySetInterval, 1000)
// 控制台直接调用 myClearInterval(timer)
封装
function mySetInterval(fn, delay) {
let timer = null
function inside() {
console.log(new Date().toLocaleString()) // 打印看看时间
clearTimeout(timer)
fn()
timer = setTimeout(inside, delay)
}
timer = setTimeout(inside, delay)
return {
clear() {
clearTimeout(timer)
}
}
}
// 清除定时器
function myClearInterval(flagTimer) {
flagTimer.clear()
}
// 测试
function testmySetInterval() {
console.log('testmySetInterval')
}
const timer = mySetInterval(testmySetInterval, 1000)
// 控制台直接调用 myClearInterval(timer)
requestAnimationFrame 代替定时器
const RAF = {
intervalTimer: null,
timeoutTimer: null,
setTimeout(cb, interval) { // 实现setTimeout功能
let now = Date.now
let stime = now()
let etime = stime
let loop = () => {
this.timeoutTimer = requestAnimationFrame(loop)
etime = now()
if (etime - stime >= interval) {
cb()
cancelAnimationFrame(this.timeoutTimer)
}
}
this.timeoutTimer = requestAnimationFrame(loop)
return this.timeoutTimer
},
clearTimeout() {
cancelAnimationFrame(this.timeoutTimer)
},
setInterval(cb, interval) { // 实现setInterval功能
let now = Date.now
let stime = now()
let etime = stime
let loop = () => {
this.intervalTimer = requestAnimationFrame(loop)
etime = now()
if (etime - stime >= interval) {
stime = now()
etime = stime
cb()
}
}
this.intervalTimer = requestAnimationFrame(loop)
return this.intervalTimer
},
clearInterval() {
cancelAnimationFrame(this.intervalTimer)
}
}
let count = 0
function a() {
console.log(count)
count++
}
RAF.setTimeout(a, 1000)
今天的文章用 setTimeout 代替 setInterval分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17196.html