const SECOND = 1000
const MINUTE = 60 * SECOND
const HOUR = 60 * MINUTE
const DAY = 24 * HOUR/*** @description: 根据传入的毫秒值格式化为时间* @param {*} time:毫秒值* @returns:{days, hours, minutes, seconds, milliseconds}*/
function parseTimeData (time) {const days = Math.floor(time / DAY)const hours = Math.floor((time % DAY) / HOUR)const minutes = Math.floor((time % HOUR) / MINUTE)const seconds = Math.floor((time % MINUTE) / SECOND)const milliseconds = Math.floor(time % SECOND)return {days,hours,minutes,seconds,milliseconds,}
}/**对比两个时间是否在秒级别上相同
*/
function isSameSecond (time1, time2) {return Math.floor(time1 / 1000) === Math.floor(time2 / 1000)
}// 使用setTimeout模拟setInterval
function loop (fn, wait) {setTimeout(() => {fn()}, wait)
}var timer = null
// 倒计时结束的时间点
var endTime = new Date('2025/4/11 17:00:00').getTime()// 秒级别倒计时(每1秒执行一次倒计时,默认为毫秒级别的倒计时)
function microTask () {timer = loop(() => {// 剩余倒计时的毫秒值const remain = endTime - Date.now()// 判断倒计时是否结束if (remain <= 0 || isSameSecond(endTime, Date.now())) {clearInterval(timer)console.log('倒计时结束了,更新页面');return}microTask()const { days, hours, minutes, seconds, milliseconds } = parseTimeData(remain)console.log(`${days}天${hours}小时${minutes}分钟${seconds}秒`)}, 1000)
}microTask()
使用setTimeout模拟setInterval的优点:
1、更精确的控制;setInterval存在一定的时间误差,(因为JS是单线程的,当主线程被其他任务占用时,可能导致setInterval的回调函数不能得到及时的执行,从而导致积累误差);但使用setTimeout时,可以在每次回调函数中重新设置setTimeout,这样可以根据上一次回调函数的执行时间来动态调整下一次的执行时间 ,从而减少误差;
2、更好的灵活性,可以在倒计时过程中根据不同的条件来暂停、恢复、取消倒计时;
3、可以动态调整每次倒计时的时间间隔wait,可以在回调函数中每次设置setTimeout时,根据业务需要调整倒计时时间间隔,而setInterval想要调整时间间隔会比较复杂