feat: 修复停止逻辑

This commit is contained in:
祁连海
2026-04-17 23:29:10 +08:00
parent adf0cccd77
commit 73f9deb53e
12 changed files with 460 additions and 63 deletions
+36 -5
View File
@@ -13,6 +13,7 @@
broadcastStopToContentScripts,
cancelPendingCommands,
clearStopRequest,
createAutoRunSessionId,
getAutoRunStatusPayload,
getErrorMessage,
getFirstUnfinishedStep,
@@ -30,6 +31,7 @@
runtime,
setState,
sleepWithStop,
throwIfAutoRunSessionStopped,
waitForRunningStepsToFinish,
} = deps;
@@ -175,6 +177,7 @@
currentRun: targetRun,
totalRuns,
attemptRun: currentRuntime.autoRunAttemptRun,
autoRunSessionId: currentRuntime.autoRunSessionId,
autoRunSkipFailures,
roundSummaries,
countdownTitle: '线程间隔中',
@@ -206,6 +209,7 @@
currentRun: targetRun,
totalRuns,
attemptRun: nextAttemptRun,
autoRunSessionId: runtime.get().autoRunSessionId,
autoRunSkipFailures,
roundSummaries,
countdownTitle: '线程间隔中',
@@ -225,12 +229,14 @@
await addLog(`自动运行异常终止:${getErrorMessage(error) || '未知错误'}`, 'error');
}
runtime.set({ autoRunActive: false });
runtime.set({ autoRunActive: false, autoRunSessionId: 0 });
await broadcastAutoRunStatus('stopped', {
currentRun: currentRuntime.autoRunCurrentRun,
totalRuns: currentRuntime.autoRunTotalRuns,
attemptRun: currentRuntime.autoRunAttemptRun,
sessionId: 0,
}, {
autoRunSessionId: 0,
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
});
@@ -250,12 +256,22 @@
return;
}
let sessionId = Number.isInteger(options.autoRunSessionId) && options.autoRunSessionId > 0
? options.autoRunSessionId
: 0;
if (sessionId) {
throwIfAutoRunSessionStopped(sessionId);
} else {
sessionId = createAutoRunSessionId();
}
clearStopRequest();
runtime.set({
autoRunActive: true,
autoRunTotalRuns: totalRuns,
autoRunCurrentRun: 0,
autoRunAttemptRun: 0,
autoRunSessionId: sessionId,
});
currentRuntime = runtime.get();
@@ -293,12 +309,14 @@
const showResumePosition = continueCurrentOnFirstAttempt || resumeCurrentRun > 1 || resumeAttemptRun > 1;
await setState({
autoRunSessionId: sessionId,
autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
...getAutoRunStatusPayload(initialPhase, {
currentRun: showResumePosition ? resumeCurrentRun : 0,
totalRuns,
attemptRun: showResumePosition ? resumeAttemptRun : 0,
sessionId,
}),
});
@@ -360,9 +378,10 @@
cloudflareDomain: prevState.cloudflareDomain,
cloudflareDomains: prevState.cloudflareDomains,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
autoRunSessionId: sessionId,
tabRegistry: {},
sourceLastUrls: {},
...getAutoRunStatusPayload('running', { currentRun: targetRun, totalRuns, attemptRun }),
...getAutoRunStatusPayload('running', { currentRun: targetRun, totalRuns, attemptRun, sessionId }),
};
await resetState();
await setState(keepSettings);
@@ -370,9 +389,10 @@
await sleepWithStop(500);
} else {
await setState({
autoRunSessionId: sessionId,
autoRunSkipFailures,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
...getAutoRunStatusPayload('running', { currentRun: targetRun, totalRuns, attemptRun }),
...getAutoRunStatusPayload('running', { currentRun: targetRun, totalRuns, attemptRun, sessionId }),
});
}
@@ -397,11 +417,12 @@
};
try {
deps.throwIfStopped();
throwIfAutoRunSessionStopped(sessionId);
await broadcastAutoRunStatus('running', {
currentRun: targetRun,
totalRuns,
attemptRun,
sessionId,
});
await runAutoSequenceFromStep(startStep, {
@@ -428,6 +449,7 @@
currentRun: targetRun,
totalRuns,
attemptRun,
sessionId: 0,
});
break;
}
@@ -453,6 +475,7 @@
currentRun: targetRun,
totalRuns,
attemptRun,
sessionId,
});
forceFreshTabsNextRun = true;
await addLog(
@@ -470,6 +493,7 @@
currentRun: targetRun,
totalRuns,
attemptRun,
sessionId: 0,
});
break;
}
@@ -493,6 +517,7 @@
currentRun: targetRun,
totalRuns,
attemptRun,
sessionId: 0,
});
break;
}
@@ -518,6 +543,7 @@
currentRun: targetRun,
totalRuns,
attemptRun,
sessionId: 0,
});
break;
}
@@ -559,6 +585,7 @@
currentRun: targetRun,
totalRuns,
attemptRun: runtime.get().autoRunAttemptRun,
sessionId: 0,
});
break;
}
@@ -584,6 +611,7 @@
currentRun: finalRuntime.autoRunCurrentRun,
totalRuns: finalRuntime.autoRunTotalRuns,
attemptRun: finalRuntime.autoRunAttemptRun,
sessionId: 0,
});
} else {
await addLog(`=== 全部 ${finalRuntime.autoRunTotalRuns} 轮已执行完成,成功 ${successfulRuns} 轮 ===`, 'ok');
@@ -591,11 +619,13 @@
currentRun: finalRuntime.autoRunTotalRuns,
totalRuns: finalRuntime.autoRunTotalRuns,
attemptRun: finalRuntime.autoRunAttemptRun,
sessionId: 0,
});
}
runtime.set({ autoRunActive: false });
runtime.set({ autoRunActive: false, autoRunSessionId: 0 });
const afterRuntime = runtime.get();
await setState({
autoRunSessionId: 0,
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
autoRunTimerPlan: null,
scheduledAutoRunPlan: null,
@@ -603,6 +633,7 @@
currentRun: deps.getStopRequested() || stoppedEarly ? afterRuntime.autoRunCurrentRun : afterRuntime.autoRunTotalRuns,
totalRuns: afterRuntime.autoRunTotalRuns,
attemptRun: afterRuntime.autoRunAttemptRun,
sessionId: 0,
}),
});
clearStopRequest();
+1
View File
@@ -137,6 +137,7 @@
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
autoRunAttemptRun: payload.attemptRun ?? 0,
autoRunSessionId: Math.max(0, Math.floor(Number(payload.sessionId ?? payload.autoRunSessionId) || 0)),
scheduledAutoRunAt: Number.isFinite(Number(payload.scheduledAt)) ? Number(payload.scheduledAt) : null,
autoRunCountdownAt: Number.isFinite(Number(payload.countdownAt)) ? Number(payload.countdownAt) : null,
autoRunCountdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''),
+72 -41
View File
@@ -12,12 +12,73 @@
LOG_PREFIX,
matchesSourceUrlFamily,
setState,
sleepWithStop,
STOP_ERROR_MESSAGE,
throwIfStopped,
} = deps;
const pendingCommands = new Map();
async function sleepOrStop(ms) {
if (typeof sleepWithStop === 'function') {
await sleepWithStop(ms);
return;
}
const start = Date.now();
while (Date.now() - start < ms) {
throwIfStopped();
await new Promise((resolve) => setTimeout(resolve, Math.min(100, ms - (Date.now() - start))));
}
}
function waitForTabUpdateComplete(tabId, timeoutMs = 30000) {
return new Promise((resolve, reject) => {
let settled = false;
let stopTimer = null;
const cleanup = () => {
if (settled) return;
settled = true;
clearTimeout(timer);
clearTimeout(stopTimer);
chrome.tabs.onUpdated.removeListener(listener);
};
const resolveSafely = () => {
cleanup();
resolve();
};
const rejectSafely = (error) => {
cleanup();
reject(error);
};
const listener = (updatedTabId, info) => {
if (updatedTabId === tabId && info.status === 'complete') {
resolveSafely();
}
};
const timer = setTimeout(resolveSafely, timeoutMs);
chrome.tabs.onUpdated.addListener(listener);
const pollStop = () => {
if (settled) return;
try {
throwIfStopped();
} catch (error) {
rejectSafely(error);
return;
}
stopTimer = setTimeout(pollStop, 100);
};
pollStop();
});
}
async function getTabRegistry() {
const state = await getState();
return state.tabRegistry || {};
@@ -179,7 +240,7 @@
} catch {
return null;
}
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
await sleepOrStop(retryDelayMs);
}
return null;
}
@@ -197,7 +258,7 @@
} catch {
return null;
}
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
await sleepOrStop(retryDelayMs);
}
return null;
}
@@ -215,7 +276,7 @@
} catch {
return null;
}
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
await sleepOrStop(retryDelayMs);
}
try {
@@ -296,7 +357,7 @@
logged = true;
}
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
await sleepOrStop(retryDelayMs);
}
throw lastError || new Error(`${getSourceLabel(source)} 内容脚本长时间未就绪。`);
@@ -418,17 +479,7 @@
if (registry[source]) registry[source].ready = false;
await setState({ tabRegistry: registry });
await chrome.tabs.reload(tabId);
await new Promise((resolve) => {
const timer = setTimeout(() => { chrome.tabs.onUpdated.removeListener(listener); resolve(); }, 30000);
const listener = (tid, info) => {
if (tid === tabId && info.status === 'complete') {
chrome.tabs.onUpdated.removeListener(listener);
clearTimeout(timer);
resolve();
}
};
chrome.tabs.onUpdated.addListener(listener);
});
await waitForTabUpdateComplete(tabId);
}
if (options.inject) {
@@ -447,7 +498,7 @@
target: { tabId },
files: options.inject,
});
await new Promise((resolve) => setTimeout(resolve, 500));
await sleepOrStop(500);
}
await rememberSourceLastUrl(source, url);
@@ -458,17 +509,7 @@
await setState({ tabRegistry: registry });
await chrome.tabs.update(tabId, { url, active: true });
await new Promise((resolve) => {
const timer = setTimeout(() => { chrome.tabs.onUpdated.removeListener(listener); resolve(); }, 30000);
const listener = (tid, info) => {
if (tid === tabId && info.status === 'complete') {
chrome.tabs.onUpdated.removeListener(listener);
clearTimeout(timer);
resolve();
}
};
chrome.tabs.onUpdated.addListener(listener);
});
await waitForTabUpdateComplete(tabId);
if (options.inject) {
if (options.injectSource) {
@@ -486,7 +527,7 @@
});
}
await new Promise((resolve) => setTimeout(resolve, 500));
await sleepOrStop(500);
await rememberSourceLastUrl(source, url);
return tabId;
}
@@ -495,17 +536,7 @@
const tab = await chrome.tabs.create({ url, active: true });
if (options.inject) {
await new Promise((resolve) => {
const timer = setTimeout(() => { chrome.tabs.onUpdated.removeListener(listener); resolve(); }, 30000);
const listener = (tabId, info) => {
if (tabId === tab.id && info.status === 'complete') {
chrome.tabs.onUpdated.removeListener(listener);
clearTimeout(timer);
resolve();
}
};
chrome.tabs.onUpdated.addListener(listener);
});
await waitForTabUpdateComplete(tab.id);
if (options.injectSource) {
await chrome.scripting.executeScript({
target: { tabId: tab.id },
@@ -580,7 +611,7 @@
logged = true;
}
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
await sleepOrStop(retryDelayMs);
}
}
@@ -628,7 +659,7 @@
injectSource: mail.injectSource,
reloadIfSameUrl: true,
});
await new Promise((resolve) => setTimeout(resolve, 800));
await sleepOrStop(800);
}
}