fix(android): setInterval impl to match c++ and prevent drift

This commit is contained in:
Nathan Walker
2025-08-28 13:08:30 -07:00
parent d98d1b9333
commit a91cc8f201

View File

@@ -58,23 +58,35 @@ export function clearTimeout(id: number): void {
} }
export function setInterval(callback: Function, milliseconds = 0, ...args): number { export function setInterval(callback: Function, milliseconds = 0, ...args): number {
// Cast to Number
milliseconds += 0; milliseconds += 0;
const id = createHandlerAndGetId(); const id = createHandlerAndGetId();
const handler = timeoutHandler; const handler = timeoutHandler;
const invoke = () => callback(...args); const invoke = () => callback(...args);
const zoneBound = zonedCallback(invoke); const zoneBound = zonedCallback(invoke);
const startOffset = milliseconds > 0 ? Date.now() % milliseconds : 0; let nextDueTime = Date.now() + milliseconds;
function nextCallMs() {
return milliseconds > 0 ? milliseconds - ((Date.now() - startOffset) % milliseconds) : milliseconds;
}
const runnable = new java.lang.Runnable({ const runnable = new java.lang.Runnable({
run: () => { run: () => {
const executionStart = Date.now();
zoneBound(); zoneBound();
if (timeoutCallbacks[id]) { 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; timeoutCallbacksCb[id] = callback;
} }
timeoutHandler.postDelayed(runnable, long(nextCallMs())); handler.postDelayed(runnable, long(milliseconds));
return id; return id;
} }