diff --git a/static/app.js b/static/app.js index 1d8cfeb..8484c39 100644 --- a/static/app.js +++ b/static/app.js @@ -117,6 +117,10 @@ createApp({ }, result: null, pollTimer: null, + statusRequestInFlight: false, + statusRequestSeq: 0, + statusAppliedSeq: 0, + requestEpoch: 0, }, restartOps: { loading: false, @@ -455,7 +459,17 @@ createApp({ return; } + // 轮询只保留一个在途请求,避免旧响应晚到后把新日志覆盖掉。 + if (this.updateOps.statusRequestInFlight) { + return; + } + const wasRunning = this.updateOps.running; + const requestSeq = this.updateOps.statusRequestSeq + 1; + const requestEpoch = this.updateOps.requestEpoch; + this.updateOps.statusRequestSeq = requestSeq; + this.updateOps.statusRequestInFlight = true; + try { const query = new URLSearchParams({ id: operationId }); const response = await fetch(`/api/ops/update-status?${query.toString()}`, { cache: "no-store" }); @@ -464,6 +478,21 @@ createApp({ throw new Error(payload.error || `HTTP ${response.status}`); } + // 新任务已经开始时,直接丢掉旧任务轮询结果。 + if (requestEpoch !== this.updateOps.requestEpoch) { + return; + } + + if (this.updateOps.operationId && this.updateOps.operationId !== operationId) { + return; + } + + // 保护日志顺序,旧轮询结果不能回退页面状态。 + if (requestSeq < this.updateOps.statusAppliedSeq) { + return; + } + this.updateOps.statusAppliedSeq = requestSeq; + this.applyUpdateOperation(payload.operation || {}); if (!this.updateOps.running) { @@ -475,6 +504,14 @@ createApp({ } } } catch (error) { + if (requestEpoch !== this.updateOps.requestEpoch) { + return; + } + + if (this.updateOps.operationId && this.updateOps.operationId !== operationId) { + return; + } + const errorMessage = error instanceof Error ? error.message : String(error); const notFound = errorMessage.includes("update operation not found") || errorMessage.includes("HTTP 404"); @@ -499,6 +536,10 @@ createApp({ this.updateOps.message = "更新状态查询失败"; } this.updateOps.error = errorMessage; + } finally { + if (requestEpoch === this.updateOps.requestEpoch && this.updateOps.statusRequestSeq === requestSeq) { + this.updateOps.statusRequestInFlight = false; + } } }, applyRefreshTimer() { @@ -848,6 +889,9 @@ createApp({ } this.stopUpdatePolling(); + this.updateOps.requestEpoch += 1; + this.updateOps.statusRequestInFlight = false; + this.updateOps.statusAppliedSeq = 0; this.updateOps.running = true; this.updateOps.error = ""; this.updateOps.message = "已提交更新任务,等待开始执行";