Skip to content

Best Practices

Use descriptive IDs for easier debugging and management:

// Good
timerService.startCooldown('skill_fireball', 5000);
timerService.schedule('explosion_wave_1', 1000, callback);
// Bad
timerService.startCooldown('cd1', 5000);
timerService.schedule('t1', 1000, callback);

Timers with the same ID will overwrite previous ones. Use unique IDs:

// Use unique IDs
const uniqueId = `explosion_${entity.id}_${Date.now()}`;
timerService.schedule(uniqueId, 1000, callback);
// Or use a counter
let timerCounter = 0;
const timerId = `timer_${++timerCounter}`;

Clean up timers and cooldowns at appropriate times:

class Entity {
private timerId: string;
onDestroy(): void {
// Clean up timer when entity is destroyed
this.timerService.cancelById(this.timerId);
}
}
class Scene {
onUnload(): void {
// Clear all when scene unloads
this.timerService.clear();
}
}

Consider setting maximum limits in production:

const timerService = createTimerService({
maxTimers: 1000,
maxCooldowns: 500
});

Use prefixes to manage related timers:

// Use unified prefix for all timers of an entity
const prefix = `entity_${entityId}_`;
timerService.schedule(`${prefix}explosion`, 1000, callback1);
timerService.schedule(`${prefix}effect`, 2000, callback2);
// Clean up by iterating with prefix
function clearEntityTimers(entityId: number): void {
const prefix = `entity_${entityId}_`;
const ids = timerService.getActiveTimerIds();
for (const id of ids) {
if (id.startsWith(prefix)) {
timerService.cancelById(id);
}
}
}
import { Component, EntitySystem, Matcher } from '@esengine/ecs-framework';
import { createTimerService, type ITimerService } from '@esengine/timer';
// Timer component
class TimerComponent extends Component {
timerService: ITimerService;
constructor() {
super();
this.timerService = createTimerService();
}
}
// Timer system
class TimerSystem extends EntitySystem {
constructor() {
super(Matcher.all(TimerComponent));
}
protected processEntity(entity: Entity, dt: number): void {
const timer = entity.getComponent(TimerComponent);
timer.timerService.update(dt);
}
}
// Cooldown component for shared cooldowns
class CooldownComponent extends Component {
constructor(public timerService: ITimerService) {
super();
}
}
// Multiple entities share the same cooldown service
const sharedCooldowns = createTimerService();
entity1.addComponent(new CooldownComponent(sharedCooldowns));
entity2.addComponent(new CooldownComponent(sharedCooldowns));
// Use service container for global timer
import { TimerServiceToken, createTimerService } from '@esengine/timer';
// Register global service
services.register(TimerServiceToken, createTimerService());
// Use in systems
class EffectSystem extends EntitySystem {
private timerService: ITimerService;
constructor(services: ServiceContainer) {
super(Matcher.all(EffectComponent));
this.timerService = services.get(TimerServiceToken);
}
applyEffect(entity: Entity, effect: Effect): void {
const id = `effect_${entity.id}_${effect.id}`;
this.timerService.schedule(id, effect.duration, () => {
entity.removeComponent(effect);
});
}
}

If you have multiple independent timer services, consider merging them:

// Not recommended: each entity has its own timer service
class BadEntity {
private timerService = createTimerService(); // Memory waste
}
// Recommended: share timer service
class GoodSystem {
private timerService = createTimerService();
addTimer(entityId: number, callback: () => void): void {
this.timerService.schedule(`entity_${entityId}`, 1000, callback);
}
}

Reuse timer IDs instead of creating new ones:

// Not recommended: create new timer every time
function onHit(): void {
timerService.schedule(`hit_${Date.now()}`, 100, showHitEffect);
}
// Recommended: cancel old timer and reuse ID
function onHit(): void {
timerService.cancelById('hit_effect');
timerService.schedule('hit_effect', 100, showHitEffect);
}

For scenarios without callbacks, cooldowns are more efficient:

// Use cooldown to limit attack frequency
if (timerService.isCooldownReady('attack')) {
attack();
timerService.startCooldown('attack', 1000);
}
// Instead of
timerService.schedule('attack_cooldown', 1000, () => {
canAttack = true;
});