fix(chat): guard activity poll against overlapping interval ticks

This commit is contained in:
matevip 2026-05-17 09:58:55 +08:00
parent 6d70834227
commit 146be51442

View File

@ -1150,6 +1150,12 @@ function applyPendingRouteAction() {
// 轮询定时器:让 ChatConsole 能实时感知外部渠道(WeChat/DingTalk/…)推进来的新消息, // 轮询定时器:让 ChatConsole 能实时感知外部渠道(WeChat/DingTalk/…)推进来的新消息,
// 无需 F5 即可看到侧栏列表更新和选中会话的消息/流状态。 // 无需 F5 即可看到侧栏列表更新和选中会话的消息/流状态。
let activityPollTimer: number | null = null let activityPollTimer: number | null = null
// Reentrancy guard: setInterval fires every ACTIVITY_POLL_MS regardless of
// whether the previous async pollActivity has finished. If one cycle runs long
// (slow reconnectStream / sluggish backend), unguarded ticks stack up and run
// concurrently, multiplying in-flight requests. This flag keeps one cycle at a
// time — late ticks become no-ops until the running cycle returns.
let activityPolling = false
const ACTIVITY_POLL_MS = 4000 const ACTIVITY_POLL_MS = 4000
// Cron progress placeholder: when a cron job is mid-run on the currently // Cron progress placeholder: when a cron job is mid-run on the currently
@ -1223,6 +1229,10 @@ function hasLocalOnlyFailedTail(): boolean {
async function pollActivity() { async function pollActivity() {
// 页面不可见时不轮询,避免切到别的标签还在空耗 // 页面不可见时不轮询,避免切到别的标签还在空耗
if (typeof document !== 'undefined' && document.hidden) return if (typeof document !== 'undefined' && document.hidden) return
// Skip when the previous cycle is still running so slow polls can't stack.
if (activityPolling) return
activityPolling = true
try {
try { try {
await loadConversations() await loadConversations()
} catch { } catch {
@ -1257,6 +1267,9 @@ async function pollActivity() {
// runs use the non-streaming chat() path, so streamStatus stays idle. // runs use the non-streaming chat() path, so streamStatus stays idle.
await refreshActiveCronRuns(cid) await refreshActiveCronRuns(cid)
} }
} finally {
activityPolling = false
}
} }
onMounted(async () => { onMounted(async () => {