fix(desktop): pollSession retries with backoff before giving up

When the phone goes to background, JS is paused and HTTP keepalive

connections die. On return, the first poll's fetch can hit "Failed to

fetch" because of a stale connection / DNS / TLS state. The original

code showed an error immediately on a single failure.

Now: same polling loop tolerates up to 5 consecutive errors with

exponential backoff (1s, 2s, 4s, 8s) before surfacing showError. Any

successful poll resets the error counter. This rides through the

typical 1-3 second post-resume blip without any user-visible failure.

Pairs with the earlier WS auto-reconnect + visibilitychange handler

(both bridge channels — WS and HTTP polling — now recover cleanly).

Bump app.js?v=87 -> 88.
This commit is contained in:
dd3xp committed 2026-06-04 04:04:20 +08:00
1 parent 633e8111a8
commit cace868fc9
2 files changed
+26 -12

No files matched your search

+25 -11
View File
@@ -1948,19 +1948,33 @@ async function pollSession(sess) {
const r = rt(sess);
if (r.polling) return;
r.polling = true;
/* 手机切后台再回前台时,第一拍 fetch 经常用着死连接秒挂(Failed to fetch),
但只要给链路 1-2 秒重建,后续就稳。原版一炸就 showError,体验糟。
改成:同一次 polling 循环里连续失败 ≥ MAX_ERRORS 次才放弃,
单次失败做指数退避(1s / 2s / 4s / 8s),够 ride through 一次后台恢复抖动。 */
const MAX_ERRORS = 5;
let consecutiveErrors = 0;
try {
do {
const res = await window.ga.pollSession(sess.bridgeSessionId || sess.id, r.lastId || 0);
if (res?.error) throw new Error(res.error.message || res.error);
const result = res.result || res;
for (const msg of (result.messages || [])) upsert(sess, msg, false);
if (result.partial) upsert(sess, result.partial, true);
const busy = result.status === 'running' || !!result.partial;
setBusy(sess, busy);
if (busy) await new Promise(z => setTimeout(z, 500));
else {
if (r.draftEl) { r.draftEl.remove(); r.draftEl = null; r.draftText = ''; }
break;
try {
const res = await window.ga.pollSession(sess.bridgeSessionId || sess.id, r.lastId || 0);
if (res?.error) throw new Error(res.error.message || res.error);
consecutiveErrors = 0;
const result = res.result || res;
for (const msg of (result.messages || [])) upsert(sess, msg, false);
if (result.partial) upsert(sess, result.partial, true);
const busy = result.status === 'running' || !!result.partial;
setBusy(sess, busy);
if (busy) await new Promise(z => setTimeout(z, 500));
else {
if (r.draftEl) { r.draftEl.remove(); r.draftEl = null; r.draftText = ''; }
break;
}
} catch (innerErr) {
consecutiveErrors++;
if (consecutiveErrors >= MAX_ERRORS) throw innerErr;
const backoff = Math.min(8000, 1000 * Math.pow(2, consecutiveErrors - 1));
await new Promise(z => setTimeout(z, backoff));
}
} while (true);
} catch (e) {
+1 -1
View File
@@ -569,6 +569,6 @@
<!-- highlight.js -->
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js" crossorigin="anonymous"></script>
<script src="phosphor-icons.js"></script>
<script src="app.js?v=87"></script>
<script src="app.js?v=88"></script>
</body>
</html>