修复素材导出并发竞态

使用同步 ref 互斥量保护集合导出和单选导出

在导出 finally 中释放互斥量并保留 state 作为忙碌 UI

新增同一事件循环连续导出的回归测试
This commit is contained in:
2026-07-27 16:37:08 +08:00
parent ea1813fcc4
commit 3b4b6cac85
2 changed files with 64 additions and 5 deletions
@@ -1,5 +1,5 @@
import JSZip from 'jszip';
import { useCallback, useState } from 'react';
import { useCallback, useRef, useState } from 'react';
import type {
CanvasAssetExportImage,
@@ -273,6 +273,7 @@ export function useImageCanvasAssetExportWorkflow({
const [assetExportStatus, setAssetExportStatus] =
useState<AssetExportStatus | null>(null);
const [isExportingAssets, setIsExportingAssets] = useState(false);
const isExportingAssetsRef = useRef(false);
const exportLayerImage = useCallback(
async (
@@ -347,7 +348,7 @@ export function useImageCanvasAssetExportWorkflow({
emptyMessage: string;
successMessage: string;
}) => {
if (isExportingAssets) {
if (isExportingAssetsRef.current) {
return;
}
const exportableLayers = targetLayers
@@ -361,6 +362,7 @@ export function useImageCanvasAssetExportWorkflow({
return;
}
isExportingAssetsRef.current = true;
setIsExportingAssets(true);
setAssetExportStatus(null);
@@ -609,10 +611,11 @@ export function useImageCanvasAssetExportWorkflow({
message: '素材导出失败',
});
} finally {
isExportingAssetsRef.current = false;
setIsExportingAssets(false);
}
},
[isExportingAssets, projectTitle],
[projectTitle],
);
const exportCanvasAssets = useCallback(
@@ -628,7 +631,7 @@ export function useImageCanvasAssetExportWorkflow({
const exportSelectedAssets = useCallback(
async (selectedLayers: CanvasLayer[]) => {
if (isExportingAssets) {
if (isExportingAssetsRef.current) {
return;
}
const exportableLayers = selectedLayers.filter(
@@ -642,11 +645,13 @@ export function useImageCanvasAssetExportWorkflow({
return;
}
if (exportableLayers.length === 1) {
isExportingAssetsRef.current = true;
setIsExportingAssets(true);
setAssetExportStatus(null);
try {
await exportLayerImage(exportableLayers[0] ?? null);
} finally {
isExportingAssetsRef.current = false;
setIsExportingAssets(false);
}
return;
@@ -658,7 +663,7 @@ export function useImageCanvasAssetExportWorkflow({
successMessage: '选中素材已导出',
});
},
[exportAssetCollection, exportLayerImage, isExportingAssets],
[exportAssetCollection, exportLayerImage],
);
return {