perf(local-asr): 引擎状态改后端事件驱动,前端零轮询#672
Conversation
issue #470:LocalAsr 前端原本 setInterval 每 5s 轮询 refreshEngineStatus。改为后端在每个 状态转换点主动 emit `local-asr:engine-changed`(load: build_local_qwen3 / preload;idle 释放: schedule_local_asr_release 的 release_if_idle==true;手动释放: release_local_asr_engine 汇聚点;keepLoadedSecs 变更),前端 listen 该事件增量更新、删掉 5s 轮询与 engineStatusTimer。 挂载时仍拉一次初值,listener 在 cleanup unlisten。LocalAsrEngineStatus 加 Clone 供 emit 复用。 helper 只用跨平台符号,不碰 Foundry/Sherpa。
PR Reviewer Guide 🔍(Review updated until commit 130257f)Here are some key observations to aid the review process:
|
|
Claude finished @appergb's task in 4m 12s —— View job PR #672 代码审查任务清单
总体评价实现正确,逻辑清晰,可以合并。 所有关键点均通过审查,下方为逐项结论与两个可选改进建议。 1. emit 覆盖完整性 ✅覆盖了所有会改变引擎状态的路径:
2. idle 释放闭包 inner Arc ✅
// coordinator.rs:3262
let inner = Arc::clone(inner); // 新增:用 owned Arc 遮蔽 &Arc 引用
tauri::async_runtime::spawn(async move {
tokio::time::sleep(dur).await;
if cache.release_if_idle(dur) {
emit_local_asr_engine_status(&inner); // 借用 move 进来的 Arc
}
});
3. 前端 listener cleanup/unlisten ✅// LocalAsr.tsx:517-543
useEffect(() => {
if (!isTauri) return
void refreshEngineStatus()
let unlisten: undefined | (() => void)
let cancelled = false
;(async () => {
const off = await listen<LocalAsrEngineStatus>("local-asr:engine-changed", ...)
if (cancelled) { off() } // 竞态:组件在 listen 返回前已卸载
else { unlisten = off }
})()...
return () => {
cancelled = true
if (unlisten) unlisten() // 正常路径
}
}, [])竞态处理完整:
与同文件 4. 删轮询后无悬空引用 ✅
5. helper 跨平台符号 ✅// coordinator.rs:3237-3248
fn emit_local_asr_engine_status(inner: &Arc<Inner>) {
let model_id = inner.local_asr_cache.loaded_model_id();
let keep_loaded_secs = inner.prefs.get().local_asr_keep_loaded_secs;
let status = crate::commands::LocalAsrEngineStatus { loaded: model_id.is_some(), model_id, keep_loaded_secs };
if let Some(app) = inner.app.lock().clone() {
let _ = app.emit("local-asr:engine-changed", &status);
}
}
可选改进(非阻塞)① // LocalAsr.tsx:1378-1385
const handleReleaseEngine = async () => {
try {
await releaseLocalAsrEngine()
await refreshEngineStatus() // backend 已 emit,此 IPC 拉取冗余
} catch (e) { ... }
}
② const handleKeepLoadedChange = async (seconds: number) => {
await setLocalAsrKeepLoadedSecs(seconds)
await refresh() // refresh() 内含 refreshEngineStatus(),同上冗余
}可改为仅刷新 settings/models,不重复拉引擎状态。同样无 bug,属于性能上的微小冗余。 结论:PR 实现正确,两个冗余 IPC 属可选优化,不影响合并。 |
|
CI 这边 Android cargo check 挂了,阻塞合并。Windows / macOS / Linux 都过了。 失败点 根因(看起来是) 修复方向(任选其一即可)
修完后麻烦 push 一下,我这边会再看 Android cargo check 是否变绿。Thanks! |
|
Persistent review updated to latest commit 130257f |
User description
源自 #470 Cloud 性能审查 #6。LocalAsr 前端原本
setInterval每 5s 轮询refreshEngineStatus。改动:后端在每个状态转换点主动 emit
local-asr:engine-changed(load、idle 释放release_if_idle==true、手动释放汇聚点、keepLoadedSecs 变更),前端listen增量更新、删掉 5s 轮询与engineStatusTimer。挂载时仍拉一次初值,cleanupunlisten。LocalAsrEngineStatus加Clone。helper 只用跨平台符号,不碰 Foundry/Sherpa。验证:macOS
cargo check+npm run build通过。PR Type
Enhancement
Description
后端主动推送引擎状态事件
前端零轮询,监听事件更新
跨平台兼容(Android 空实现)
Diagram Walkthrough
flowchart LR subgraph Backend [Backend Rust] coordinator["coordinator.rs"] end subgraph Frontend [Frontend React] localAsr["LocalAsr.tsx"] end coordinator -- "emit 'local-asr:engine-changed'" --> localAsrFile Walkthrough
local_asr.rs
派生 Clone 并触发状态推送openless-all/app/src-tauri/src/commands/local_asr.rs
coordinator.rs
实现事件发射函数与跨平台适配openless-all/app/src-tauri/src/coordinator.rs
LocalAsr.tsx
前端零轮询监听引擎状态openless-all/app/src/pages/LocalAsr.tsx