perf(webview2): avoid queuing fire-and-forget scripts
This commit is contained in:
+42
-7
@@ -214,7 +214,7 @@ class Wv2Session:
|
||||
self._js_pump = QTimer()
|
||||
self._js_pump.setInterval(25)
|
||||
self._js_pump.timeout.connect(self._js_pump_tick)
|
||||
self._js_pump.start()
|
||||
# 无回调脚本不进入队列;没有待处理任务时不需要常驻唤醒 UI 线程。
|
||||
# 🆕 预热:立即导航 about:blank,让 msedgewebview2 进程/GPU 在 UI 构建期间冷启动
|
||||
# (实测本机首次真实页面导航需 12-15s,预热后降到 ~1s)
|
||||
try:
|
||||
@@ -339,8 +339,27 @@ class Wv2Session:
|
||||
print("[WV2] navigate error:", ex)
|
||||
|
||||
def execute_js(self, script: str):
|
||||
"""fire-and-forget(ChatBridge.run_js 的替换,JS 文本完全同构)"""
|
||||
self._js_run(script, None)
|
||||
"""执行无需返回值的脚本,不进入结果轮询队列。
|
||||
|
||||
流式正文每个 token 都会走这里。旧实现统一使用
|
||||
``ExecuteScriptWithResultAsync`` 并把任务放入 ``_js_pending``,高频
|
||||
输出时会在渲染器和 Python 侧同时堆积大量无用结果;WebView2 原生
|
||||
``ExecuteScriptAsync`` 已经提供了真正的 fire-and-forget 路径。
|
||||
"""
|
||||
try:
|
||||
execute_async = getattr(self.core, "ExecuteScriptAsync")
|
||||
except AttributeError:
|
||||
# 兼容旧版/测试替身未暴露 ExecuteScriptAsync 的情况。即使只能
|
||||
# 使用带结果 API,也直接丢弃 Task,不把无用结果放进轮询队列。
|
||||
try:
|
||||
getattr(self.core, "ExecuteScriptWithResultAsync")(script)
|
||||
except BaseException as ex:
|
||||
print("[WV2] execute_js fallback error:", ex)
|
||||
return
|
||||
try:
|
||||
execute_async(script)
|
||||
except BaseException as ex:
|
||||
print("[WV2] execute_js error:", ex)
|
||||
|
||||
def execute_js_async(self, script: str, cb):
|
||||
"""带回调执行:真异步,回调在主线程定时器 tick 中发出(绝不阻塞)"""
|
||||
@@ -358,13 +377,25 @@ class Wv2Session:
|
||||
pass
|
||||
return
|
||||
self._js_pending.append([task, cb, time.time()])
|
||||
try:
|
||||
if not self._js_pump.isActive():
|
||||
self._js_pump.start()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _js_pump_tick(self):
|
||||
if not self._js_pending:
|
||||
try:
|
||||
self._js_pump.stop()
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
import json
|
||||
remaining = []
|
||||
for task, cb, t0 in self._js_pending:
|
||||
# 先摘下本批次;回调中再次入队的任务写入新的 _js_pending,不能被
|
||||
# 本轮收尾赋值覆盖。
|
||||
pending = self._js_pending
|
||||
self._js_pending = []
|
||||
for task, cb, t0 in pending:
|
||||
done = False
|
||||
try:
|
||||
done = task.IsCompleted
|
||||
@@ -373,7 +404,7 @@ class Wv2Session:
|
||||
if not done and time.time() - t0 > 10:
|
||||
done = True # 10s 安全超时(渲染器死亡时不永久卡队列)
|
||||
if not done:
|
||||
remaining.append([task, cb, t0])
|
||||
self._js_pending.append([task, cb, t0])
|
||||
continue
|
||||
result = None
|
||||
try:
|
||||
@@ -395,7 +426,11 @@ class Wv2Session:
|
||||
cb(result)
|
||||
except Exception as ex:
|
||||
print("[WV2] js callback error:", ex)
|
||||
self._js_pending = remaining
|
||||
if not self._js_pending:
|
||||
try:
|
||||
self._js_pump.stop()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user