task-83: 统一不同页面的轮询去重、in-flight 合并和终态清理
新增纯 TS 任务轮询协调器 task-polling-coordinator:重复 add 去重、 并发 runOnce 合并为单次请求并共享结果、markTerminal 幂等终态清理 (只回调一次)、maxTasks 上限拒绝超出条目。useTaskProgressLoop 新增 可选 coordinator 选项,add/remove/终态路径委托协调器统一处理(默认 不启用)。测试用 node:test 覆盖 8 个场景,含 3 路并发合并断言。
This commit is contained in:
@@ -9,6 +9,10 @@ import {
|
||||
createProgressResponseCache,
|
||||
type ProgressResponseCache,
|
||||
} from '@/shared/progress-response-cache'
|
||||
import {
|
||||
createTaskPollingCoordinator,
|
||||
type TaskPollingCoordinator,
|
||||
} from '@/shared/task-polling-coordinator'
|
||||
|
||||
/**
|
||||
* 通用任务进度轮询组合式函数。
|
||||
@@ -66,6 +70,12 @@ export interface TaskProgressLoopOptions<TDetail> {
|
||||
* 轮询间隙复用最近一次进度快照。
|
||||
*/
|
||||
progressCache?: ProgressResponseCache<TDetail>
|
||||
/**
|
||||
* 可选轮询协调器(Task 83):传入后 add/remove/终态清理委托给协调器
|
||||
* 统一去重、合并并发请求;终态任务经协调器回调后从轮询集合移除,
|
||||
* 同一任务只触发一次终态回调。
|
||||
*/
|
||||
coordinator?: TaskPollingCoordinator
|
||||
}
|
||||
|
||||
export interface TaskProgressLoopHandle<TDetail> {
|
||||
@@ -116,6 +126,7 @@ export function useTaskProgressLoop<TDetail>(
|
||||
const isTerminal = options.isTerminal ?? DEFAULT_TERMINAL
|
||||
const intervalMs = options.getIntervalMs ?? getTaskPollIntervalMs
|
||||
const baseline = options.onBaseline ? createTaskPollingBaseline() : null
|
||||
const coordinator = options.coordinator ?? null
|
||||
|
||||
const taskIds = ref<number[]>(readIdsFromStorage(options.storageKey))
|
||||
const taskStatuses = ref<Record<number, string>>({})
|
||||
@@ -129,6 +140,7 @@ export function useTaskProgressLoop<TDetail>(
|
||||
|
||||
function add(taskId: number) {
|
||||
if (!Number.isFinite(taskId) || taskId <= 0) return
|
||||
if (coordinator && !coordinator.add(taskId)) return
|
||||
if (taskIds.value.includes(taskId)) return
|
||||
taskIds.value = [...taskIds.value, taskId]
|
||||
persist()
|
||||
@@ -136,6 +148,7 @@ export function useTaskProgressLoop<TDetail>(
|
||||
}
|
||||
|
||||
function remove(taskId: number) {
|
||||
coordinator?.remove(taskId)
|
||||
if (!taskIds.value.includes(taskId)) return
|
||||
taskIds.value = taskIds.value.filter((id) => id !== taskId)
|
||||
persist()
|
||||
@@ -187,6 +200,7 @@ export function useTaskProgressLoop<TDetail>(
|
||||
}
|
||||
taskStatuses.value = nextStatuses
|
||||
for (const event of terminalEvents) {
|
||||
coordinator?.markTerminal(event.taskId)
|
||||
remove(event.taskId)
|
||||
try {
|
||||
await options.onTerminal?.(event.taskId, event.detail, event.status)
|
||||
|
||||
Reference in New Issue
Block a user