Download src/hooks/useJobPolling.ts from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 3.47 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/hooks/useJobPolling.ts
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/hooks/useJobPolling.ts
-
curl -L -o useJobPolling.ts https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/hooks/useJobPolling.ts
3.47 kB
| "use client"; | |
| import { useEffect, useEffectEvent, useRef, useState } from "react"; | |
| import { getPublicPollIntervalMs } from "@/lib/env"; | |
| type UseJobPollingOptions<T> = { | |
| enabled: boolean; | |
| fetcher: () => Promise<T>; | |
| shouldContinue: (value: T) => boolean; | |
| onData: (value: T) => void; | |
| onError?: (error: unknown) => void; | |
| onTimeout?: () => void; | |
| intervalMs?: number; | |
| timeoutMs?: number; | |
| }; | |
| export function useJobPolling<T>({ | |
| enabled, | |
| fetcher, | |
| shouldContinue, | |
| onData, | |
| onError, | |
| onTimeout, | |
| intervalMs = getPublicPollIntervalMs(), | |
| timeoutMs = 10 * 60 * 1000, | |
| }: UseJobPollingOptions<T>) { | |
| const [isPolling, setIsPolling] = useState(false); | |
| const startedAtRef = useRef<number | null>(null); | |
| const requestActiveRef = useRef(false); | |
| const stoppedRef = useRef(false); | |
| const timedOutRef = useRef(false); | |
| const consecutiveErrorsRef = useRef(0); | |
| const generationRef = useRef(0); | |
| // A render runs for minutes; a single failed poll (HF cold-start, a network | |
| // blip, a brief 5xx) must NOT abort the whole job in the UI while the backend | |
| // keeps rendering fine. Tolerate transient errors and only give up after | |
| // several in a row. A successful poll resets the counter. | |
| const MAX_CONSECUTIVE_ERRORS = 6; | |
| const pollOnce = useEffectEvent(async () => { | |
| if (requestActiveRef.current) return; | |
| const generation = generationRef.current; | |
| requestActiveRef.current = true; | |
| try { | |
| const data = await fetcher(); | |
| if (generation !== generationRef.current || stoppedRef.current) return; | |
| consecutiveErrorsRef.current = 0; | |
| onData(data); | |
| if (!shouldContinue(data)) { | |
| stoppedRef.current = true; | |
| setIsPolling(false); | |
| } | |
| } catch (error) { | |
| if (generation !== generationRef.current || stoppedRef.current) return; | |
| consecutiveErrorsRef.current += 1; | |
| if (consecutiveErrorsRef.current >= MAX_CONSECUTIVE_ERRORS) { | |
| stoppedRef.current = true; | |
| setIsPolling(false); | |
| onError?.(error); | |
| } | |
| // otherwise: swallow and let the next interval retry | |
| } finally { | |
| requestActiveRef.current = false; | |
| } | |
| }); | |
| const markTimedOutIfNeeded = useEffectEvent(() => { | |
| const startedAt = startedAtRef.current ?? Date.now(); | |
| if (Date.now() - startedAt <= timeoutMs) return false; | |
| stoppedRef.current = true; | |
| setIsPolling(false); | |
| if (!timedOutRef.current) { | |
| timedOutRef.current = true; | |
| onTimeout?.(); | |
| } | |
| return true; | |
| }); | |
| useEffect(() => { | |
| if (!enabled) { | |
| stoppedRef.current = true; | |
| const timeoutId = window.setTimeout(() => setIsPolling(false), 0); | |
| return () => window.clearTimeout(timeoutId); | |
| } | |
| stoppedRef.current = false; | |
| timedOutRef.current = false; | |
| consecutiveErrorsRef.current = 0; | |
| startedAtRef.current = Date.now(); | |
| const startTimeoutId = window.setTimeout(() => setIsPolling(true), 0); | |
| const initialPollTimeoutId = window.setTimeout(() => void pollOnce(), 0); | |
| const intervalId = window.setInterval(() => { | |
| if (stoppedRef.current) return; | |
| if (markTimedOutIfNeeded()) return; | |
| setIsPolling(true); | |
| void pollOnce(); | |
| }, intervalMs); | |
| return () => { | |
| generationRef.current += 1; | |
| stoppedRef.current = true; | |
| window.clearTimeout(startTimeoutId); | |
| window.clearTimeout(initialPollTimeoutId); | |
| window.clearInterval(intervalId); | |
| }; | |
| }, [enabled, intervalMs]); | |
| return { isPolling }; | |
| } | |