Skip to content

Commit 964ef70

Browse files
committed
fix: Improve clock accuracy
Instead of using setInterval to get a tick every second, starting at a random point an slowly drifting a ms or 2 every few updates, set a timeout for the number of ms until the next update is due. For example, if we have an interval of 1000ms (1s), and we start at 12:00:00.123, we will set a timeout of 877ms so that the next update should be at exactly 12:00:01.000. In practice, depending on how busy the browser’s event loop is etc. it might be at e.g. 12:00:01.002, in which case the next update will set a timeout of 998ms.
1 parent 8bba9ef commit 964ef70

File tree

1 file changed

+21
-4
lines changed
  • packages/webui/src/client/lib

1 file changed

+21
-4
lines changed

packages/webui/src/client/lib/lib.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,33 @@ export function useDebounce<K>(value: K, delay: number, shouldUpdate?: (oldVal:
178178
return debouncedValue
179179
}
180180

181+
/**
182+
* This hook returns the current time and updates it at the specified refresh period.
183+
* It uses a timeout to schedule the next update based on the refresh period.
184+
* The time is calculated as the current time in milliseconds since the Unix epoch.
185+
* The hook also cleans up the timeout when the component using it unmounts.
186+
*
187+
* @param refreshPeriod - The period in milliseconds at which the current time should be refreshed.
188+
* @returns The current time in milliseconds since the Unix epoch.
189+
*/
181190
export function useCurrentTime(refreshPeriod = 1000): number {
182191
const [time, setTime] = useState(getCurrentTime())
192+
const timeoutId = useRef<ReturnType<typeof setTimeout> | null>(null)
183193

184194
useEffect(() => {
185-
const interval = setInterval(() => {
186-
setTime(getCurrentTime())
187-
}, refreshPeriod)
195+
function updateTime() {
196+
const current = getCurrentTime()
197+
setTime(current)
198+
const delay = refreshPeriod - (current % refreshPeriod)
199+
timeoutId.current = setTimeout(updateTime, delay)
200+
}
201+
202+
updateTime() // Initial call to start the timer
188203

189204
return () => {
190-
clearInterval(interval)
205+
if (timeoutId.current) {
206+
clearTimeout(timeoutId.current) // Cleanup on unmount
207+
}
191208
}
192209
}, [refreshPeriod])
193210

0 commit comments

Comments
 (0)