Skip to content

Commit 1179f89

Browse files
committed
feat: 前端请求并发控制重构,优化订阅 flow 拉取中断与重试行为
- 新增 `src/utils/requestConcurrency.ts`,统一管理前端请求队列、并发上限(默认 3)和可配置等待时间(`concurrencyWaitTime`),并支持优先级与 `AbortSignal` 取消。 - `useSubsStore` 中重构 `fetchFlows`:新增 per-flow 版本号 + abort 控制,增加重复触发和页面切换时的旧请求拦截,移除旧的 `executeAsyncTasks` 实现。 - 路由和请求端联动:离开 `/subs` 时自动取消未完成 flow 请求;`getFlow` 增加 `signal` 参数并按新并发任务模型执行。 - 将 `subs`、`files`、`share`、`settings`、`archive`、`artifacts` 的关键请求改为 `runFrontendRequestTask`,减少竞态并统一行为。 - 新增 `concurrencyWaitTime` 配置链路:支持 URL query 参数写入、My 页面配置项、提示文案,并同步更新中英文+俄文文案。 - `My` 页面新增并发等待时间输入及说明弹窗,界面可视化配置该参数。
1 parent f503bdc commit 1179f89

12 files changed

Lines changed: 488 additions & 96 deletions

File tree

src/api/subs/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ export function useSubsApi() {
2828
method: 'get',
2929
});
3030
},
31-
getFlow: (name: string): AxiosPromise<MyAxiosRes> => {
31+
getFlow: (name: string, signal?: AbortSignal): AxiosPromise<MyAxiosRes> => {
3232
return request({
3333
url: `/api/sub/flow/${encodeURIComponent(name)}`,
3434
method: 'get',
35+
signal,
3536
});
3637
},
3738
getSubInfo: (data: NodeInfo): AxiosPromise<MyAxiosRes> => {

src/hooks/useHostAPI.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,24 @@ export const useHostAPI = () => {
194194
}
195195
}
196196
}
197+
// 处理 concurrencyWaitTime 参数
198+
const concurrencyWaitTime = query
199+
.slice(1)
200+
.split('&')
201+
.map(i => i.split('='))
202+
.find(i => i[0] === 'concurrencyWaitTime');
203+
if (concurrencyWaitTime) {
204+
const value = parseInt(concurrencyWaitTime[1], 10);
205+
if (!isNaN(value)) {
206+
if (value > 0) {
207+
console.log(`设置并发等待时间 ${value}`)
208+
localStorage.setItem('concurrencyWaitTime', value.toString());
209+
} else {
210+
console.log(`清除并发等待时间设置`)
211+
localStorage.removeItem('concurrencyWaitTime');
212+
}
213+
}
214+
}
197215
// 处理 timeout 参数
198216
const timeout = query
199217
.slice(1)

src/locales/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ export default {
736736
scriptCacheTtl: 'Script Cache TTL, default: 172800(s)',
737737
logsMaxCount: 'Max stored logs, default: 0=off',
738738
concurrency: 'Concurrency Limit(default: 3)',
739+
concurrencyWaitTime: 'Concurrency Wait Time, default: 0(ms)',
739740
apiCheckTimeout: 'API Check Timeout, default: 3000(ms)',
740741
apiRequestTimeout: 'Front-End Request Timeout, default: 50000(ms)',
741742
noGithubUser: "Not set GitHub username",

src/locales/ru.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ export default {
785785
"scriptCacheTtl": "TTL кэша скриптов, по умолчанию: 172800 (сек)",
786786
"logsMaxCount": "Макс. количество логов, 0 = отключено",
787787
"concurrency": "Лимит параллельных запросов (по умолчанию: 3)",
788+
"concurrencyWaitTime": "Время ожидания параллельных запросов, по умолчанию: 0 (мс)",
788789
"apiCheckTimeout": "Таймаут проверки API, по умолчанию: 3000 (мс)",
789790
"apiRequestTimeout": "Таймаут запросов интерфейса, по умолчанию: 50000 (мс)",
790791
"noGithubUser": "Не задано имя пользователя GitHub",

src/locales/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,7 @@ export default {
700700
scriptCacheTtl: '脚本缓存(单位: 秒, 默认: 172800)',
701701
logsMaxCount: '最大保存日志条数(默认: 0=关闭)',
702702
concurrency: '并发数(默认: 3)',
703+
concurrencyWaitTime: '并发等待时间(单位: 毫秒, 默认: 0)',
703704
apiCheckTimeout: 'API 检测超时(单位: 毫秒, 默认: 3000)',
704705
apiRequestTimeout: '前端请求超时(单位: 毫秒, 默认: 50000)',
705706
noGithubUser: '未配置 GitHub 用户名',

src/router/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useFilesApi } from '@/api/files';
55

66
import AppLayout from '@/layout/AppLayout.vue';
77
import { useGlobalStore } from '@/store/global';
8+
import { useSubsStore } from '@/store/subs';
89
import { initStores } from '@/utils/initApp';
910
import My from '@/views/My.vue';
1011

@@ -335,6 +336,9 @@ router.afterEach(async (to, from) => {
335336
router.beforeEach((to, from) => {
336337
document.title = 'Sub Store';
337338
// console.log(`beforeEach ${from.path} => ${to.path}`)
339+
if (to?.path !== '/subs') {
340+
useSubsStore().cancelFetchFlows();
341+
}
338342
if (!globalStore) {
339343
globalStore = useGlobalStore();
340344
}

src/store/archive.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useAppNotifyStore } from '@/store/appNotify';
66
import { useArtifactsStore } from '@/store/artifacts';
77
import { useSubsStore } from '@/store/subs';
88
import { normalizeArchiveEntry } from '@/utils/archive';
9+
import { runFrontendRequestTask } from '@/utils/requestConcurrency';
910

1011
const archiveApi = useArchiveApi();
1112
const { t } = i18n.global;
@@ -22,7 +23,7 @@ export const useArchiveStore = defineStore('archiveStore', {
2223
actions: {
2324
async fetchEntries() {
2425
try {
25-
const res = await archiveApi.getEntries();
26+
const res = await runFrontendRequestTask(() => archiveApi.getEntries(), 'archive.getEntries');
2627
if (res?.data?.status === 'success') {
2728
this.entries = (res.data.data || []).map(normalizeArchiveEntry);
2829
return true;

src/store/artifacts.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useArtifactsApi } from '@/api/artifacts';
22
import i18n from '@/locales';
33
import { useAppNotifyStore } from '@/store/appNotify';
4+
import { runFrontendRequestTask } from '@/utils/requestConcurrency';
45
import { defineStore } from 'pinia';
56

67
const { t } = i18n.global;
@@ -15,7 +16,7 @@ export const useArtifactsStore = defineStore('artifactsStore', {
1516
getters: {},
1617
actions: {
1718
async fetchArtifactsData() {
18-
const { data } = await artifactsApi.getArtifacts();
19+
const { data } = await runFrontendRequestTask(() => artifactsApi.getArtifacts(), 'artifacts.getArtifacts');
1920
if (data.status === 'success') {
2021
this.artifacts = data.data;
2122
}

src/store/settings.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useSettingsApi } from "@/api/settings";
22
import i18n from "@/locales";
33
import { useGlobalStore } from '@/store/global';
44
import { useAppNotifyStore } from "@/store/appNotify";
5+
import { runFrontendRequestTask } from "@/utils/requestConcurrency";
56
import { Toast } from "@nutui/nutui";
67
import { defineStore } from "pinia";
78
// import { useEnvApi } from '@/api/env';
@@ -197,7 +198,7 @@ export const useSettingsStore = defineStore("settingsStore", {
197198
},
198199
async fetchSettings() {
199200
const { showNotify } = useAppNotifyStore();
200-
const res = await settingsApi.getSettings();
201+
const res = await runFrontendRequestTask(() => settingsApi.getSettings(), "settings.getSettings");
201202
if (res?.data?.status === "success" && res?.data?.data) {
202203
this.syncPlatform = res.data.data.syncPlatform || "";
203204
this.gistToken = res.data.data.gistToken || "";

0 commit comments

Comments
 (0)