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 = QTimer()
|
||||||
self._js_pump.setInterval(25)
|
self._js_pump.setInterval(25)
|
||||||
self._js_pump.timeout.connect(self._js_pump_tick)
|
self._js_pump.timeout.connect(self._js_pump_tick)
|
||||||
self._js_pump.start()
|
# 无回调脚本不进入队列;没有待处理任务时不需要常驻唤醒 UI 线程。
|
||||||
# 🆕 预热:立即导航 about:blank,让 msedgewebview2 进程/GPU 在 UI 构建期间冷启动
|
# 🆕 预热:立即导航 about:blank,让 msedgewebview2 进程/GPU 在 UI 构建期间冷启动
|
||||||
# (实测本机首次真实页面导航需 12-15s,预热后降到 ~1s)
|
# (实测本机首次真实页面导航需 12-15s,预热后降到 ~1s)
|
||||||
try:
|
try:
|
||||||
@@ -339,8 +339,27 @@ class Wv2Session:
|
|||||||
print("[WV2] navigate error:", ex)
|
print("[WV2] navigate error:", ex)
|
||||||
|
|
||||||
def execute_js(self, script: str):
|
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):
|
def execute_js_async(self, script: str, cb):
|
||||||
"""带回调执行:真异步,回调在主线程定时器 tick 中发出(绝不阻塞)"""
|
"""带回调执行:真异步,回调在主线程定时器 tick 中发出(绝不阻塞)"""
|
||||||
@@ -358,13 +377,25 @@ class Wv2Session:
|
|||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
self._js_pending.append([task, cb, time.time()])
|
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):
|
def _js_pump_tick(self):
|
||||||
if not self._js_pending:
|
if not self._js_pending:
|
||||||
|
try:
|
||||||
|
self._js_pump.stop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return
|
return
|
||||||
import json
|
import json
|
||||||
remaining = []
|
# 先摘下本批次;回调中再次入队的任务写入新的 _js_pending,不能被
|
||||||
for task, cb, t0 in self._js_pending:
|
# 本轮收尾赋值覆盖。
|
||||||
|
pending = self._js_pending
|
||||||
|
self._js_pending = []
|
||||||
|
for task, cb, t0 in pending:
|
||||||
done = False
|
done = False
|
||||||
try:
|
try:
|
||||||
done = task.IsCompleted
|
done = task.IsCompleted
|
||||||
@@ -373,7 +404,7 @@ class Wv2Session:
|
|||||||
if not done and time.time() - t0 > 10:
|
if not done and time.time() - t0 > 10:
|
||||||
done = True # 10s 安全超时(渲染器死亡时不永久卡队列)
|
done = True # 10s 安全超时(渲染器死亡时不永久卡队列)
|
||||||
if not done:
|
if not done:
|
||||||
remaining.append([task, cb, t0])
|
self._js_pending.append([task, cb, t0])
|
||||||
continue
|
continue
|
||||||
result = None
|
result = None
|
||||||
try:
|
try:
|
||||||
@@ -395,7 +426,11 @@ class Wv2Session:
|
|||||||
cb(result)
|
cb(result)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[WV2] js callback error:", 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):
|
def close(self):
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user