javascript - 倒计时钩每分钟损失 1 秒
问题描述
我正在我的 react-native 应用程序中实现倒计时,但有些东西无法正常工作。
倒计时似乎每分钟损失 1 秒(正如您在 gif 中看到的那样,它在 33 和 31 之间跳跃)
这是代码:
import {
differenceInDays,
differenceInHours,
differenceInMinutes,
differenceInSeconds,
isBefore,
parseISO,
} from 'date-fns'
import { useEffect, useState } from 'react'
type CountdownResult = {
days: number
hours: number
minutes: number
seconds: number
}
const calculateInitialDuration = (endDate: string, today: Date): CountdownResult => {
const futureDate = new Date(endDate)
const days = differenceInDays(futureDate, today)
const hours = differenceInHours(futureDate, today) % 24
const minutes = differenceInMinutes(futureDate, today) % 60
const seconds = differenceInSeconds(futureDate, today) % 60
return { days, hours, minutes, seconds }
}
const EXPIREDRESULT: CountdownResult = { days: 0, hours: 0, minutes: 0, seconds: 0 }
// TODO: FIXME: sometimes the countdown jumps directly between 2 seconds
// even if the real time passed is 1 second
// this was happening before the refactor too
const useCountdown = (endDate: string): CountdownResult => {
const today = new Date()
const formattedEndDate = parseISO(endDate)
// doing this because at the beginning countdown seems stuck on the first second
// maybe there is a better solution for this problem
const initialCountdown = calculateInitialDuration(endDate, today)
initialCountdown.seconds++
const [time, setTime] = useState(isBefore(formattedEndDate, today) ? EXPIREDRESULT : initialCountdown)
useEffect(() => {
if (isBefore(formattedEndDate, today)) return
const intervalId = setInterval(() => {
setTime(calculateInitialDuration(endDate, today))
}, 1000)
return (): void => clearInterval(intervalId)
}, [time])
return time
}
export default useCountdown
endDate
是遵循ISO 8601格式的字符串。我正在使用date-fns但我也尝试了基本的 javascript 实现,错误仍然相同。
另一个奇怪的事情是倒计时,一开始,第一秒就卡住了一秒(这就是我创建initialCountdown
变量的原因),但实际上我不喜欢这个解决方案。
有小费吗?错误在哪里?提前致谢。
解决方案
目前,您假设setInterval()
每 1,000 毫秒触发一次回调。
setInterval(() => {
setTime(calculateInitialDuration(endDate, today))
}, 1000)
不幸的是,浏览器必须做的所有其他事情,都不能保证它会。
为了获得更高的准确性,您需要做的是重复使用setTimeout()
计算设置超时的时间。
let timeout;
const start = (() => {
// IIFE because func needs to be able to reference itself!
let func = () => {
// Do whatever you need to do here
let now = new Date();
let timeToNextSecond = 1000 - (now.getTime() % 1000);
console.log('Now: ', now, 'TimeToNext: ', timeToNextSecond);
timeout = setTimeout(func, timeToNextSecond);
};
return func;
})();
const stop = () => clearTimeout(timeout);
start();
// wait 10 seconds(ish)
setTimeout(stop, 10000);
如果您运行此程序,您将看到后续超时在下一秒开始后不久运行。假设浏览器没有陷入其他事情,它将每秒运行一次。
想法:我想setInterval
在幕后做这样的事情,只是有一个固定的超时导致漂移。
推荐阅读
- django - 初学者 Django / Python / Postgres 开发人员在没有模型数据输出到 html 文件的情况下苦苦挣扎
- docker - Docker Swarm 监听管理器和工作节点的 ISP ip
- r - 使用嵌套查找表在第二个表中查找高于阈值的值并在 R 中量化它们
- python - 可变类型的链式赋值
- reactjs - 使用redux获取数据后无法登录
- javascript - 将 JSON 对象解析为 VueJs2 中的数组,然后渲染数组以选择选项
- css - 背景图像动画在 Safari 反应应用程序中不起作用
- javascript - Cloudflare 的 HTMLRewriter 入门
- android - 如何在 Activity 中使用 listModel.get(position)?
- python - 有选择地在数据框中添加列的值