feat: 修复停止逻辑
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user