From 92bca1c72819652ccf66fbd74a089cd26b632ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= Date: Sat, 27 Jun 2026 17:47:58 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=20Worker=20?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=B1=BB=E5=9E=8B=E4=BB=A5=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=E4=BB=BB=E5=8A=A1=20ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TestDataGenerator/hooks/useGenerator.ts | 29 +++++++++++----- src/types/testDataGenerator.ts | 20 ++++++++--- src/workers/generator.worker.ts | 33 ++++++++++++++++--- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/pages/TestDataGenerator/hooks/useGenerator.ts b/src/pages/TestDataGenerator/hooks/useGenerator.ts index 7d38bd5..e1d3221 100644 --- a/src/pages/TestDataGenerator/hooks/useGenerator.ts +++ b/src/pages/TestDataGenerator/hooks/useGenerator.ts @@ -8,7 +8,8 @@ import type { FieldConfig, GenerateResult, GenerateProgress, - WorkerMessage, + WorkerRequestMessage, + WorkerResponseMessage, } from '@/types/testDataGenerator'; export interface UseGeneratorReturn { @@ -35,6 +36,7 @@ export function useGenerator(): UseGeneratorReturn { const [error, setError] = useState(null); const workerRef = useRef(null); + const generationIdRef = useRef(0); // 清理 Worker useEffect(() => { @@ -58,22 +60,30 @@ export function useGenerator(): UseGeneratorReturn { type: 'module', }); - worker.onmessage = (e: MessageEvent) => { + worker.onmessage = (e: MessageEvent) => { const data = e.data; const { type } = data; + if (data.generationId !== generationIdRef.current) { + return; + } + switch (type) { case 'progress': - setProgress(data.payload as GenerateProgress); + setProgress(data.payload); break; case 'complete': setIsGenerating(false); - setResult(data.payload as GenerateResult); + if (data.payload.success) { + setResult(data.payload); + } else if (data.payload.error && data.payload.error !== '生成已取消') { + setError(data.payload.error); + } setProgress(null); break; case 'error': setIsGenerating(false); - setError((data.payload as { error: string }).error); + setError(data.payload.error); setProgress(null); break; } @@ -100,15 +110,17 @@ export function useGenerator(): UseGeneratorReturn { (fields: FieldConfig[], count: number, csvMode = false) => { if (isGenerating) return; + const generationId = ++generationIdRef.current; + setIsGenerating(true); setProgress(null); setResult(null); setError(null); const worker = getWorker(); - const message: WorkerMessage = { + const message: WorkerRequestMessage = { type: 'start', - payload: { fields, count, csvMode }, + payload: { generationId, fields, count, csvMode }, }; worker.postMessage(message); }, @@ -120,7 +132,8 @@ export function useGenerator(): UseGeneratorReturn { */ const cancel = useCallback(() => { if (workerRef.current && isGenerating) { - const message: WorkerMessage = { type: 'cancel' }; + ++generationIdRef.current; + const message: WorkerRequestMessage = { type: 'cancel' }; workerRef.current.postMessage(message); setIsGenerating(false); setProgress(null); diff --git a/src/types/testDataGenerator.ts b/src/types/testDataGenerator.ts index 8d141bd..71a096e 100644 --- a/src/types/testDataGenerator.ts +++ b/src/types/testDataGenerator.ts @@ -156,19 +156,29 @@ export interface GenerateProgress { } /** - * Worker 消息类型 + * Worker 请求消息类型 */ -export type WorkerMessage = +export type WorkerRequestMessage = | { type: 'start'; payload: WorkerStartPayload } - | { type: 'progress'; payload: GenerateProgress } - | { type: 'complete'; payload: GenerateResult } - | { type: 'error'; payload: { error: string } } | { type: 'cancel' }; +/** + * Worker 响应消息类型 + */ +export type WorkerResponseMessage = + | { type: 'progress'; generationId: number; payload: GenerateProgress } + | { type: 'complete'; generationId: number; payload: GenerateResult } + | { type: 'error'; generationId: number; payload: { error: string } }; + +/** @deprecated 使用 WorkerRequestMessage | WorkerResponseMessage */ +export type WorkerMessage = WorkerRequestMessage | WorkerResponseMessage; + /** * Worker 启动参数 */ export interface WorkerStartPayload { + /** 生成任务 ID,用于忽略过期 Worker 响应 */ + generationId: number; /** 字段配置列表 */ fields: FieldConfig[]; /** 生成数量 */ diff --git a/src/workers/generator.worker.ts b/src/workers/generator.worker.ts index 5795cee..2780b6f 100644 --- a/src/workers/generator.worker.ts +++ b/src/workers/generator.worker.ts @@ -6,11 +6,14 @@ import { getGeneratorById } from '@/lib/generators'; import type { FieldConfig, - WorkerMessage, + WorkerRequestMessage, GenerateResult, GenerateProgress, } from '@/types/testDataGenerator'; +/** 每生成 N 行让出一次事件循环,以便处理 cancel 消息 */ +const YIELD_EVERY = 100; + // 生成结果缓存 let generatedData: Record[] = []; let isCancelled = false; @@ -18,7 +21,7 @@ let isCancelled = false; /** * Worker 消息处理器 */ -self.onmessage = async (e: MessageEvent) => { +self.onmessage = async (e: MessageEvent) => { const data = e.data; const { type } = data; @@ -37,11 +40,12 @@ self.onmessage = async (e: MessageEvent) => { * 处理开始生成消息 */ async function handleStart(payload: { + generationId: number; fields: FieldConfig[]; count: number; csvMode: boolean; }): Promise { - const { fields, count } = payload; + const { generationId, fields, count } = payload; generatedData = []; try { @@ -51,6 +55,7 @@ async function handleStart(payload: { if (!generator) { self.postMessage({ type: 'error', + generationId, payload: { error: `生成器 "${field.generatorId}" 不存在` }, }); return; @@ -65,6 +70,7 @@ async function handleStart(payload: { if (isCancelled) { self.postMessage({ type: 'complete', + generationId, payload: { success: false, error: '生成已取消', @@ -136,7 +142,23 @@ async function handleStart(payload: { total: count, estimatedTimeLeft: Math.round(((Date.now() - startTime) / (i + 1)) * (count - i - 1)), }; - self.postMessage({ type: 'progress', payload: progress }); + self.postMessage({ type: 'progress', generationId, payload: progress }); + } + + // 定期让出事件循环,使 cancel 消息能被处理 + if ((i + 1) % YIELD_EVERY === 0) { + await new Promise((resolve) => setTimeout(resolve, 0)); + if (isCancelled) { + self.postMessage({ + type: 'complete', + generationId, + payload: { + success: false, + error: '生成已取消', + }, + }); + return; + } } } @@ -155,10 +177,11 @@ async function handleStart(payload: { }, }; - self.postMessage({ type: 'complete', payload: result }); + self.postMessage({ type: 'complete', generationId, payload: result }); } catch (error) { self.postMessage({ type: 'error', + generationId, payload: { error: `生成失败: ${error}` }, }); }