From e07f142932260797a2da62bd132f62eff217d82d Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Thu, 28 Aug 2025 13:08:30 -0700 Subject: [PATCH] fix(android): setInterval impl to match c++ and prevent drift --- packages/core/timer/index.android.ts | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/core/timer/index.android.ts b/packages/core/timer/index.android.ts index 8ac0ac4929..4c0eb4074b 100644 --- a/packages/core/timer/index.android.ts +++ b/packages/core/timer/index.android.ts @@ -58,23 +58,35 @@ export function clearTimeout(id: number): void { } export function setInterval(callback: Function, milliseconds = 0, ...args): number { - // Cast to Number milliseconds += 0; const id = createHandlerAndGetId(); const handler = timeoutHandler; const invoke = () => callback(...args); const zoneBound = zonedCallback(invoke); - const startOffset = milliseconds > 0 ? Date.now() % milliseconds : 0; - function nextCallMs() { - return milliseconds > 0 ? milliseconds - ((Date.now() - startOffset) % milliseconds) : milliseconds; - } + let nextDueTime = Date.now() + milliseconds; const runnable = new java.lang.Runnable({ run: () => { + const executionStart = Date.now(); zoneBound(); + if (timeoutCallbacks[id]) { - handler.postDelayed(runnable, long(nextCallMs())); + const executionTime = Date.now() - executionStart; + + // Update the next due time based on when this callback was supposed to execute + nextDueTime += milliseconds; + + // If the callback took longer than the interval, skip ahead to avoid catch-up + const now = Date.now(); + if (nextDueTime <= now) { + // Calculate how many intervals we should skip + const missedIntervals = Math.floor((now - nextDueTime) / milliseconds); + nextDueTime += (missedIntervals + 1) * milliseconds; + } + + const delay = Math.max(0, nextDueTime - now); + handler.postDelayed(runnable, long(delay)); } }, }); @@ -84,8 +96,7 @@ export function setInterval(callback: Function, milliseconds = 0, ...args): numb timeoutCallbacksCb[id] = callback; } - timeoutHandler.postDelayed(runnable, long(nextCallMs())); - + handler.postDelayed(runnable, long(milliseconds)); return id; }