返回 AiToEarn
useGenerationPolling.ts
根目录 / project / aitoearn-web / src / components / draft-box / hooks / useGenerationPolling.ts
1 import type { DraftGenerationTask } from '@/api/ai/ai.types'
2 import { useCallback, useEffect, useRef } from 'react'
3 import { apiQueryDraftGenerationTasks } from '@/api/ai/ai.api'
4
5 const QUERY_BATCH_SIZE = 10
6
7 function chunkTaskIds(taskIds: string[]) {
8 const chunks: string[][] = []
9 for (let i = 0; i < taskIds.length; i += QUERY_BATCH_SIZE) {
10 chunks.push(taskIds.slice(i, i + QUERY_BATCH_SIZE))
11 }
12 return chunks
13 }
14
15 interface UseGenerationPollingOptions {
16 /** 是否启用轮询 */
17 enabled: boolean
18 /** 需要查询的生成任务 ID */
19 taskIds: string[]
20 /** 轮询间隔(毫秒) */
21 interval?: number
22 /** 每次轮询返回任务详情 */
23 onTasksUpdate: (tasks: DraftGenerationTask[]) => void
24 /** count 减少(有任务完成)时的回调 */
25 onTaskCompleted: () => void
26 /** 每次轮询更新 count */
27 onCountUpdate?: (count: number) => void
28 }
29
30 export function useGenerationPolling({
31 enabled,
32 taskIds,
33 interval = 5000,
34 onTasksUpdate,
35 onTaskCompleted,
36 onCountUpdate,
37 }: UseGenerationPollingOptions) {
38 const prevCountRef = useRef<number | null>(null)
39 const isRequestRunningRef = useRef(false)
40 const taskIdsRef = useRef(taskIds)
41 taskIdsRef.current = taskIds
42
43 // 用 ref 保持回调最新引用,避免轮询重启
44 const onTaskCompletedRef = useRef(onTaskCompleted)
45 onTaskCompletedRef.current = onTaskCompleted
46 const onCountUpdateRef = useRef(onCountUpdate)
47 onCountUpdateRef.current = onCountUpdate
48 const onTasksUpdateRef = useRef(onTasksUpdate)
49 onTasksUpdateRef.current = onTasksUpdate
50
51 const poll = useCallback(async () => {
52 if (isRequestRunningRef.current) {
53 return
54 }
55 const currentTaskIds = taskIdsRef.current
56 if (currentTaskIds.length === 0) {
57 return
58 }
59
60 isRequestRunningRef.current = true
61
62 try {
63 const results = await Promise.all(
64 chunkTaskIds(currentTaskIds).map(async (ids) => {
65 const res = await apiQueryDraftGenerationTasks(ids)
66 return res?.data || []
67 }),
68 )
69 const tasks = results.flat()
70 const newCount = tasks.filter(task => task.status === 'generating').length
71 onTasksUpdateRef.current(tasks)
72 onCountUpdateRef.current?.(newCount)
73
74 // 如果之前有记录的 count 且新 count 更小,说明有任务完成了
75 if (prevCountRef.current !== null && newCount < prevCountRef.current) {
76 onTaskCompletedRef.current()
77 }
78
79 prevCountRef.current = newCount
80 }
81 catch (error) {
82 // 静默失败
83 }
84 finally {
85 isRequestRunningRef.current = false
86 }
87 }, [])
88
89 useEffect(() => {
90 if (!enabled || taskIds.length === 0) {
91 prevCountRef.current = null
92 return
93 }
94
95 // 立即执行一次
96 poll()
97
98 const timer = setInterval(poll, interval)
99 return () => {
100 clearInterval(timer)
101 }
102 }, [enabled, interval, poll, taskIds.length])
103 }
104
104 lines TYPESCRIPT