Files
omniai-web/src/api/taskSubscription.ts
T

97 lines
2.7 KiB
TypeScript
Raw Normal View History

2026-06-02 12:38:01 +08:00
import { aiGenerationClient } from "./aiGenerationClient";
export interface TaskProgressEvent {
taskId: string;
status: string;
progress: number;
resultUrl?: string | null;
error?: string | null;
}
export interface WaitForTaskOptions {
onProgress?: (event: TaskProgressEvent) => void;
abortRef?: { current: boolean };
timeoutMs?: number;
}
const POLL_INTERVAL = 3000;
const DEFAULT_TIMEOUT = 30 * 60 * 1000;
export function waitForTask(
taskId: string,
options: WaitForTaskOptions = {},
): Promise<string | null> {
const { onProgress, abortRef, timeoutMs = DEFAULT_TIMEOUT } = options;
return new Promise((resolve, reject) => {
let settled = false;
let cleanup: (() => void) | null = null;
let timeoutId: ReturnType<typeof setTimeout> | null = null;
let sseConnected = false;
let fallbackTimerId: ReturnType<typeof setTimeout> | null = null;
const settle = (fn: () => void) => {
if (settled) return;
settled = true;
if (timeoutId) clearTimeout(timeoutId);
if (fallbackTimerId) clearTimeout(fallbackTimerId);
if (cleanup) cleanup();
fn();
};
timeoutId = setTimeout(
() => settle(() => reject(new Error("等待任务结果超时,请稍后在任务历史中查看"))),
timeoutMs,
);
const handleUpdate = (event: TaskProgressEvent) => {
if (settled) return;
if (abortRef?.current) {
settle(() => resolve(null));
return;
}
onProgress?.(event);
if (event.status === "completed") {
settle(() => resolve(event.resultUrl || null));
} else if (event.status === "failed" || event.status === "cancelled") {
2026-06-04 21:07:48 +08:00
settle(() => reject(new Error(event.error || "任务失败,请稍后重试")));
2026-06-02 12:38:01 +08:00
}
};
cleanup = aiGenerationClient.subscribeTaskStatus(taskId, handleUpdate);
sseConnected = true;
fallbackTimerId = setTimeout(() => {
if (settled || !sseConnected) return;
if (cleanup) cleanup();
startPolling();
}, 5000);
function startPolling() {
const poll = async () => {
while (!settled) {
2026-06-04 21:07:48 +08:00
if (abortRef?.current) {
settle(() => resolve(null));
return;
}
2026-06-02 12:38:01 +08:00
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
if (settled || abortRef?.current) return;
try {
const task = await aiGenerationClient.getTaskStatus(taskId);
handleUpdate({
taskId,
status: task.status,
progress: task.progress || 0,
resultUrl: task.resultUrl,
error: task.error,
});
} catch (e) {
if (!settled) settle(() => reject(e));
}
}
};
poll();
}
});
}