47 lines
1.4 KiB
TypeScript
47 lines
1.4 KiB
TypeScript
/**
|
|
* Runs the server's recurring tasks. Each module that needs one exports a
|
|
* {@link Job}, and `main.ts` passes them all to {@link startJobs} when it runs
|
|
* directly, so nothing recurs in the tests, which call a job's `run` when
|
|
* they need it.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
/** A recurring task, exported by its module for `main.ts` to start. */
|
|
export interface Job {
|
|
/** Names it in the log, as `[digest]`. */
|
|
name: string;
|
|
/** How long from one run's start to the next. */
|
|
everyMs: number;
|
|
/** How long after the start the first run waits, `everyMs` by default. */
|
|
firstAfterMs?: number;
|
|
/** Logs what it did itself, if anything; its errors are logged here. */
|
|
run(): void | Promise<void>;
|
|
}
|
|
|
|
/**
|
|
* Starts running each of `jobs` on its own timer. A run still going when the
|
|
* next is due makes that one skip, and a run that throws or rejects is logged
|
|
* and the next one runs anyway.
|
|
*/
|
|
export function startJobs(jobs: readonly Job[]): void {
|
|
for (const job of jobs) {
|
|
let running = false;
|
|
const tick = async () => {
|
|
if (running) return;
|
|
running = true;
|
|
try {
|
|
await job.run();
|
|
} catch (err) {
|
|
console.error(`[${job.name}] run failed:`, err);
|
|
} finally {
|
|
running = false;
|
|
}
|
|
};
|
|
setTimeout(() => {
|
|
tick();
|
|
setInterval(tick, job.everyMs);
|
|
}, job.firstAfterMs ?? job.everyMs);
|
|
}
|
|
}
|