js -- setTimeout 实现倒计时不准确的问题
setTimeout、setInterval 属于定时触发器线程属于 macrotask,它的回调会受到GUI渲染、事件触发、http请求、等的影响。所以这两个不适合做精准的定时。最好的方法是定时矫正,用 new Date(targetDate:Date - new Date ) 格式化成你需要的时分秒即可。
-
setTimeout、setInterval 等 timer 的执行时间点会受到js同步代码、microtask、ui rendering等的影响,导致设置的delay expired之后无法马上执行;
-
timer有 节流(throttle) 机制,可以去mdn详细了解下。总之,不要依赖 timer 去做准确的计时计算。PS: 深入使用setTimeout等方法前建议先了解下event loop机制。
重点就是你的 setTimeout 回调函数执行的时机,比较说倒计时,每次我们的回调函数执行时,都要获取当前时间,与函数执行完毕之后的时间,它们两个时间的差就是你的函数执行时间,然后再用 1s 减去函数执行时间,就是下次 setTimeout 执行的时机;
不建议用setInterval,假如函数执行时间较长,则会跳过一次执行。建议用setTimeout递归的方式实现
start = (new Date()).getTime()
setTimeout(() => {
b = undefined;
for(var a = 0 ; a < 10000 ; a++) {
b = a;
}
end = (new Date()).getTime()
excuTime = end - start;
console.log('excuTime: ', excuTime) // 输出:1003
}, 1000)
下次 setTimeout 的执行时机应该时 1000-excuTime ,通过这样不断修正,来达到尽量的精准性。
倒计时 计算时间差(服务器获取时间)
this.nowDate = Math.floor((new Date().getTime()) / 1000) * 1000 或 res.data.second * 1000 (服务器获取的10位时间戳)
handleCountTime() {
clearTimeout(this.timer)
const t1 = Date.now()
let c1 = 0 // 递归次数
const interval = 1000 // 间隔
const sellStartTime = this.goodsDetail.sellStartTime // 售卖开始时间
if (!sellStartTime) return null
// let nowDate = this.systemTime
// if(sellStartTime < nowDate) return false
var timeDifference = parseInt(sellStartTime - this.nowDate) // 相差的总秒数
const twentyFour = 24 * 60 * 60 * 1000
if (timeDifference > twentyFour) return null
this.countdown.h = Math.floor(timeDifference / 1000 / 60 / 60 % 24)
this.countdown.m = Math.floor(timeDifference / 1000 / 60 % 60)
this.countdown.s = Math.floor(timeDifference / 1000 % 60)
this.countdown.h = this.countdown.h < 10 ? '0' + this.countdown.h : this.countdown.h
this.countdown.m = this.countdown.m < 10 ? '0' + this.countdown.m : this.countdown.m
this.countdown.s = this.countdown.s < 10 ? '0' + this.countdown.s : this.countdown.s
if (timeDifference < 0) {
this.showCountdown = false
if (this.goodsDetail.sellStatus === 0) { // 改变页面状态
this.goodsDetail.sellStatus = 1
}
clearTimeout(this.timer)
} else if (timeDifference === 0) {
this.showCountdown = true
if (this.goodsDetail.sellStatus === 0) {
this.goodsDetail.sellStatus = 1
}
this.nowDate += 1000
// 计算误差
const offset = Date.now() - (t1 + c1 * interval)
const nextTime = interval - offset
c1++
this.timer = setTimeout(this.handleCountTime, nextTime)
} else {
this.showCountdown = true
this.nowDate += 1000
// 计算误差
const offset = Date.now() - (t1 + c1 * interval)
const nextTime = interval - offset
c1++
this.timer = setTimeout(this.handleCountTime, nextTime)
}
}
获取本地 new Date() 的方法详见 https://www.cnblogs.com/lisaShare/p/11164541.html
分类:
JavaScript
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
2018-08-30 网易im即时通讯 移动端嵌入web