Skip to content

perf(timers): eye-care evaluation timer polls every second even when feature disabled #720

Description

@404-Page-Found

Description

initEyeCareReminder() in eye-care-reminder.js:521 unconditionally creates a 1 Hz timer:

checkTimer = createTimer(evaluateReminder, 1000) // CHECK_INTERVAL_MS = 1000

regardless of whether the feature is enabled. The default state is {enabled:false} (eye-care-reminder.js:90 and settings.js:1399), so every newly opened tab spawns a timer that wakes every second to read localStorage('eyeCareReminder'), call Date.now(), and run clearExpiredActiveReminder/syncVisibleElapsed/evaluateReminder, even though evaluateReminder immediately returns when !enabled.

VisibilityInterval does pause while hidden, but while any tab is visible the timer still burns CPU and I/O forever for a disabled feature. refreshEyeCareReminder() also ends with unconditional evaluateReminder() and never manages checkTimer lifecycle. The disabled path in evaluateReminder() (438–449) and clearActiveReminderState() (471–483) only stop countdownTimer, never call stopCheckTimer().

File

src/features/eye-care-reminder.js around line 521 (and 518 for refreshEyeCareReminder, 438 for disabled early return).

Reproduction

  1. Fresh profile — never enable Eye-care reminder (default disabled).
  2. Open 5 New Tab pages.
  3. Profile with DevTools Performance or add console.log inside evaluateReminder.
  4. Observe ~5 wakeups/second even though the feature is off; each does localStorage.getItem + JSON.parse. After 8 hours of idle visible tabs this is ~144k unnecessary wakeups per tab.

Expected: checkTimer should only be started when loadState().enabled === true and stopped when the state becomes disabled.

The sibling bug is pomodoro.js:476 where non-leader tabs poll via raw setInterval(reconcileTimerState, 1000) even when document.hidden === true (should use VisibilityInterval), compounding the waste with multiple tabs.

Suggested fix

Guard initEyeCareReminder and refreshEyeCareReminder to start checkTimer only when enabled, and call stopCheckTimer() immediately when state.enabled becomes false. For pomodoro, replace the raw setInterval with new VisibilityInterval.

Duplicate check

Searched existing issues: #701–#705 are all games perf, #706–#710 are feature requests. No existing issue mentions eye-care-reminder, checkTimer, or evaluation polling. Not a duplicate.

Category / Severity

Category: performance | Severity: medium (high waste at scale)

Verified: lines 521–526 show unconditional createTimer(evaluateReminder, 1000) with no guard on loadState().enabled; default state is {enabled:false}; disabled path never stops checkTimer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingenhancementNew feature or requestp3Low priority; minor issue or feature

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions