对账整段设界,并把提交前置预算变成真正的取消
对账用 Promise.all 同时等项目快照与 refreshAssetLibrary,而 loadEditorAssetLibrary 没有 timeoutMs。catch 也接不住永不 settle:await 不返回则 finally 永远不执行, 占位归属登记与源图层锁都释放不掉,而到期清理又豁免已登记的占位,页面永久停在 generating。这个洞是上一条留下的——给 loadEditorProject 加界时只覆盖了同一个 Promise.all 里两个 await 中的一个。 改为给整段对账设 75 秒上界,往里加新的 await 自动受约束。超时解析为 null 而不 拒绝:这段在 catch 内,抛出会穿出 async 函数变成未处理 rejection;null 则落进 既有的「快照读取失败」分支。 前置预算原先只是 Promise.race,超时后上传继续跑到 confirm 并注册对象。把同一个 AbortSignal 贯穿凭证、直传与 confirm 三步,由预算到期时 abort——只停其中一步会 留下半成品。未做重试等待与 flush 的贯穿:收益是少产生不可见的存储孤儿,而 flush 已有 60 秒上界。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,9 @@ export type EditorMediaAssetUploadOptions = {
|
||||
pathSegments?: string[];
|
||||
entityId?: string;
|
||||
metadata?: Record<string, string>;
|
||||
// 中文注释:贯穿凭证、直传与 confirm 三步。只中止直传会留下未 confirm 的 OSS 对象,
|
||||
// 只中止 confirm 又会让实体已写入却无记录——要停就整条链一起停。
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
|
||||
const EDITOR_MEDIA_READ_EXPIRE_SECONDS = 60 * 60;
|
||||
@@ -86,6 +89,7 @@ export async function uploadEditorMediaAssetObjectFile(
|
||||
const ticket = await requestJson<DirectUploadTicketResponse>(
|
||||
'/api/assets/direct-upload-tickets',
|
||||
{
|
||||
signal: options.signal,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -112,11 +116,17 @@ export async function uploadEditorMediaAssetObjectFile(
|
||||
{ retry: EDITOR_REQUEST_RETRY_OPTIONS },
|
||||
);
|
||||
|
||||
await postEditorDirectUploadFile(ticket.upload, file, '上传素材失败');
|
||||
await postEditorDirectUploadFile(
|
||||
ticket.upload,
|
||||
file,
|
||||
'上传素材失败',
|
||||
options.signal,
|
||||
);
|
||||
|
||||
const confirmed = await requestJson<ConfirmAssetObjectResponse>(
|
||||
'/api/assets/objects/confirm',
|
||||
{
|
||||
signal: options.signal,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
|
||||
@@ -53,11 +53,15 @@ export async function postEditorDirectUploadFile(
|
||||
upload: EditorDirectUploadTarget,
|
||||
file: File,
|
||||
errorMessage: string,
|
||||
// 中文注释:调用方超时后仅停止 await 是不够的——这一步是唯一往 OSS 写实体的动作,
|
||||
// 不取消的话被放弃的上传会继续跑完并注册对象,用户重试再产生一份,留下不可见的孤儿。
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
for (let attempt = 0; ; attempt += 1) {
|
||||
const response = await fetch(upload.host, {
|
||||
method: 'POST',
|
||||
body: buildDirectUploadFormData(upload, file),
|
||||
signal,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
|
||||
Reference in New Issue
Block a user