fix repeating timers in GodOfTimeService

This commit is contained in:
Hazelnoot 2025-10-02 15:34:23 -04:00
parent e3a582b9ba
commit a1ef776f6f

View file

@ -31,21 +31,27 @@ export class GodOfTimeService extends TimeService<GodsOwnTimer> {
public set now(value: number) {
// Moving backwards is allowed, for now.
if (value > this._now) {
// Fire all expiring timers in chronological order.
const expiringTimers = this.timers
.values()
.filter(t => t.expiresAt >= value)
.toArray()
.sort((a, b) => a.expiresAt - b.expiresAt);
// Since timers may repeat, we need to loop this.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
while (true) {
// Fire all expiring timers in chronological order.
const expiringTimers = this.timers
.values()
.filter(t => t.expiresAt <= value)
.toArray()
.sort((a, b) => a.expiresAt - b.expiresAt);
// Since we sorted the list, this will progressively increase "now" as we handle later and later events.
for (const timer of expiringTimers) {
// When the timer fires, "now" should equal the time that was originally waited for.
this._now = timer.expiresAt;
// Stop when everything is caught up
if (expiringTimers.length === 0) {
break;
}
// Cleanup first in case timer throws an exception.
this.timers.delete(timer.timerId);
timer.callback();
// Since we sorted the list, this will progressively increase "now" as we handle later and later events.
for (const timer of expiringTimers) {
// When the timer fires, "now" should equal the time that was originally waited for.
this._now = timer.expiresAt;
this.runTimer(timer);
}
}
}
@ -53,6 +59,20 @@ export class GodOfTimeService extends TimeService<GodsOwnTimer> {
this._now = value;
}
private runTimer(timer: GodsOwnTimer): void {
// Cleanup first in case timer throws an exception.
if (timer.repeating) {
timer.expiresAt = this._now + timer.delay;
} else {
this.timers.delete(timer.timerId);
}
// Fire the actual callback.
// If it throws an error, then processing will stop halfway.
// This is good, since it means the adjustment can be retried safely.
timer.callback();
}
/**
* Get or set the current time, as a JavaScript Date object.
*/