Skip to content

perf(marketplace,localasr): 清理冗余定时器#674

Merged
appergb merged 1 commit into
betafrom
fix/470-redundant-timers
Jun 16, 2026
Merged

perf(marketplace,localasr): 清理冗余定时器#674
appergb merged 1 commit into
betafrom
fix/470-redundant-timers

Conversation

@appergb

@appergb appergb commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

User description

源自 #470 Cloud 性能审查 #8/#9

  • Marketplace:上传后双重 setTimeout(1.5s+5s) 去重为单次 5s——乐观更新已即时反映「我的发布」,延时刷新仅做服务端状态校准,1.5s 那次纯冗余。
  • LocalAsrscheduleScrollGuardRestore 三连 setTimeout(0/80/200ms) 仅删等价的 0ms 那枪(已被紧随 rAF 覆盖),保守保留 80/200ms 兜住 rAF 之后的异步重排,避免滚动跳变回归。

验证npm run build 通过。


PR Type

Enhancement


Description

  • 移除 MarketPlace 上传后的冗余 1.5s 定时器,仅保留 5s 兜底刷新

  • 移除 LocalAsr 滚动保护中的 setTimeout(…,0),保留 80ms/200ms 兜住异步重排


Diagram Walkthrough

flowchart LR
  A["Marketplace upload"] --> B["Optimistic update (immediate)"]
  A --> C["Schedule refresh (5s)"]
  C --> D["Server state calibration"]
  A -.-> E["Removed: 1.5s refresh"]
  F["LocalAsr scroll guard"] --> G["rAF restore (0~32ms)"]
  F --> H["setTimeout(80ms) restore"]
  F --> I["setTimeout(200ms) restore"]
  G -.-> J["Removed: setTimeout(0ms)"]
Loading

File Walkthrough

Relevant files
Performance optimization
LocalAsr.tsx
移除冗余的 setTimeout(…,0)                                                                       

openless-all/app/src/pages/LocalAsr.tsx

+2/-1     
Marketplace.tsx
去除上传后双重 setTimeout,仅保留 5s                                                               

openless-all/app/src/pages/Marketplace.tsx

+3/-2     

issue #470:
- Marketplace 上传后双重 setTimeout(1.5s+5s) 去重为单次 5s——乐观更新已即时反映「我的
  发布」,延时刷新仅做服务端状态校准,1.5s 那次纯冗余。
- LocalAsr scheduleScrollGuardRestore 三连 setTimeout(0/80/200ms) 仅删等价的 0ms 那枪
  (已被紧随的 rAF 覆盖),保留 80/200ms 兜住 rAF 之后的异步重排,避免滚动跳变回归。
@appergb

appergb commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

@claude 请审查本 PR(#470 #8/#9:清理冗余定时器)。重点核查 Marketplace 去重后上传列表仍会更新(乐观更新覆盖)、LocalAsr 仅删 0ms 那枪是否真等价(rAF 覆盖)、80/200ms 保留是否足以防滚动跳变。

@github-actions

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

470 - Partially compliant

Compliant requirements:

(empty)

Non-compliant requirements:

  • PR 移除了 Marketplace 和 LocalAsr 中的冗余定时器,未修复 Windows 语音胶囊不显示的问题

Requires further human verification:

(empty)

9 - Partially compliant

Compliant requirements:

(empty)

Non-compliant requirements:

  • PR 未引入任何 Linux 特定适配代码,仅对 Marketplace 和 LocalAsr 进行了定时器清理

Requires further human verification:

(empty)

8 - Partially compliant

Compliant requirements:

(empty)

Non-compliant requirements:

  • PR 未新增或修改上述任何模块,仅清理了现有代码中的冗余定时器

Requires further human verification:

(empty)

⏱️ Estimated effort to review: 1 🔵⚪⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ No major issues detected

@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown

Claude finished @appergb's task in 2m 13s —— View job


PR 审查:清理冗余定时器 (#470 #8/#9)

  • 读取仓库指引 (CLAUDE.md)
  • 获取 PR 差异
  • 核查 Marketplace 乐观更新逻辑
  • 核查 LocalAsr 0ms 删除等价性(rAF 覆盖)
  • 核查 80/200ms 保留是否足以防滚动跳变
  • 发布审查结论

Marketplace.tsx — 去重 1.5s+5s → 单次 5s

乐观更新覆盖:✅ 正确

上传成功(收到 200)后,setMyPacks() 立即将新包插入列表顶部(或原地覆盖同 id 条目),用户无需等任何 setTimeout 即可在「我的发布」看到新条目(state 由服务端 result.state 直接携带,通常为 'pending')。1.5s 那次 refresh + refreshMyPacks 是多余的——在绝大多数情况下后端此时尚未更新审核状态,拿回来的数据和乐观值一致,徒增一次网络请求。

边界情况 ⚠️(低风险)

// Marketplace.tsx:340, 345
const localPack = localPacks.find(p => p.id === packId);
// ...
if (localPack && currentLogin) {   // 两个条件都需满足才走乐观更新
    setMyPacks(...)
}

localPacknull(包被中途从本地列表删除)或 currentLogin 尚未载入,乐观路径会被跳过,用户必须等 5s 才看到新条目。原来是等 1.5s。这是一个极罕见的竞态窗口,在正常上传流程(先选包才触发上传)中几乎不会发生,可以接受。

5s 延时合理性:✅
5s 给后端充裕时间落库并更新审核状态(如快速自动审核),一次兜底刷新足够。refresh() 同时刷新公开列表,用户若在此期间打开市场主视图,5s 内不会看到新包——可接受的最终一致性代价。


LocalAsr.tsx — 删除 setTimeout(0) 是否等价

rAF 覆盖 0ms 的时序分析:基本等价,有微小理论缺口

触发点 原始 修改后
~0ms setTimeout(0)
~16ms rAF → restoreScrollGuard rAF → restoreScrollGuard
~32ms nested rAF nested rAF
~80ms setTimeout(80) setTimeout(80)
~200ms setTimeout(200) setTimeout(200)
每次 render useLayoutEffect(() => restoreScrollGuard()) 同左 ✓

setTimeout(0) 在浏览器实际延时约 0–4ms,早于 rAF(~16ms)触发。删除后存在约 0–16ms 的覆盖空白:若在此窗口内有非 React 驱动的同步 DOM 重排(如第三方库、WebView 原生行为),restoreScrollGuard 会晚 ~16ms 才被调用,理论上可能闪现一帧滚动跳变。

但考虑到:

  1. useLayoutEffect(无依赖数组,每次渲染后同步运行)已覆盖所有 React-driven reflow;
  2. 触发场景(Sherpa 模型加载/切换引起的异步重排)属于 IO 异步,不会在同步帧内立即触发滚动;
  3. 原有 rAF + nested rAF 在实际使用中未见问题。

结论:0ms 那枪删除等价性 ✅(实践中覆盖,理论缺口可忽略)

80/200ms 保留是否足以防滚动跳变:✅

80ms 覆盖快速异步资源(如 Sherpa 引擎返回的文本触发的快速重排),200ms 覆盖更慢的异步操作(图片晚加载、WebView 字体渲染)。两个守卫足够兜住 rAF 之后的异步 layout shift。

一处值得关注的细节(非本 PR 引入,既有问题):

// LocalAsr.tsx:203-211
const scheduleScrollGuardRestore = () => {
    window.setTimeout(restoreScrollGuard, 80)   // ← 返回的 id 未存储
    window.setTimeout(restoreScrollGuard, 200)  // ← 同上
    window.requestAnimationFrame(() => { ... })
}

80/200ms 的 timer id 没有被追踪——deactivate 只清理 scrollGuardTimer(10s 那个),不清理这两个。调用 deactivate(用户滚动/点击/10s 超时)后,scrollGuard.current 会被置 null,restoreScrollGuard 开头的 if (!guard) return 会让它们变成 no-op,不会导致错误,但有轻微的泄漏感知。这是既有行为,与本 PR 无关,可在后续清理。


总结

改动 正确性 建议
Marketplace: 去掉 1.5s timer ✅ 可合入 可在 PR 描述补充"localPack 为 null 时退化为 5s"以备查
LocalAsr: 删 setTimeout(0) ✅ 可合入 0–16ms 理论缺口无实际影响
LocalAsr: 保留 80/200ms ✅ 保守正确

整体评价:改动保守、论证充分,可以合入。

@appergb appergb merged commit 16e0f27 into beta Jun 16, 2026
5 checks passed
@appergb appergb deleted the fix/470-redundant-timers branch June 16, 2026 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant