@clickup/ent-framework
Version:
A PostgreSQL graph-database-alike library with microsharding and row-level security
79 lines • 3 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.WeakTicker = void 0;
/**
* A perf efficient approximate scheduler which doesn't retain the scheduled
* objects in memory, so they remain subject for GC.
*/
class WeakTicker {
constructor() {
this.slots = new Map();
}
/**
* Schedules a call to target.onTick() to be called periodically, every tickMs
* approximately.
* - The target scheduled will NOT be retained from GC. This is the main
* difference with setInterval() and the reason why we accept an object, not
* a closure.
* - The 1st call to target.onTick() will happen between 0 and tickMs from
* now: this is the second difference from setInterval(). Then, next calls
* will follow. The current tick number is passed to onTick().
* - If the same target is scheduled again, its tick number will be reset to
* 0, as if it's scheduled the very 1st time. The 2nd scheduling is cheap
* (just 2 map lookups), so an object can be rescheduled-over as many times
* as needed.
* - If target.onTick() returns "unschedule", the target will be unscheduled.
*/
schedule(target, tickMs) {
// We DO NOT use any closures here! Otherwise, target would be retained in
// that closures, and it won't be garbage collected.
let slot = this.slots.get(tickMs);
if (!slot) {
slot = {
refs: new Set(),
nextTickNos: new WeakMap(),
interval: setInterval(this.onTick.bind(this, tickMs), tickMs).unref(),
};
this.slots.set(tickMs, slot);
}
if (!slot.nextTickNos.has(target)) {
const ref = new WeakRef(target);
slot.refs.add(ref);
}
slot.nextTickNos.set(target, 0);
}
/**
* Returns true if there are no targets scheduled at the moment.
*/
isEmpty() {
return this.slots.size === 0;
}
/**
* Called by internal setInterval().
*/
onTick(tickMs) {
const slot = this.slots.get(tickMs);
for (const ref of slot.refs) {
const target = ref.deref();
if (target) {
const nextTickNo = slot.nextTickNos.get(target);
if (target.onTick(nextTickNo, tickMs) === "unschedule") {
slot.refs.delete(ref);
slot.nextTickNos.delete(target);
}
slot.nextTickNos.set(target, nextTickNo + 1);
}
else {
slot.refs.delete(ref);
// Target was garbage collected, which means it is already auto-removed
// from slot.nextTickNos WeakMap too.
}
}
if (slot.refs.size === 0) {
clearInterval(slot.interval);
this.slots.delete(tickMs);
}
}
}
exports.WeakTicker = WeakTicker;
//# sourceMappingURL=WeakTicker.js.map
;