【问题标题】:Countdown hook loses 1 second every minute倒计时钩每分钟损失 1 秒
【发布时间】:2020-06-30 13:06:19
【问题描述】:

我正在我的 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() 不保证每 1000 毫秒执行一次!
  • 感谢@phuzi 的评论,您有什么建议吗?不同的实现?
  • 看我的回答 ;)

标签: javascript reactjs react-native


【解决方案1】:

目前您假设setInterval() 每 1000 毫秒触发一次回调。

    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 在幕后做了类似的事情,只是有一个固定的超时导致漂移。

【讨论】:

  • 谢谢。实际上,在我的setInterval 上添加timeToNextSecond 机制也可以解决问题(并且让我删除了initialCountdown 变量!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-26
  • 1970-01-01
相关资源
最近更新 更多