index.ts 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import { Instance } from "../project/instance"
  2. import { Log } from "../util/log"
  3. export namespace Scheduler {
  4. const log = Log.create({ service: "scheduler" })
  5. export type Task = {
  6. id: string
  7. interval: number
  8. run: () => Promise<void>
  9. scope?: "instance" | "global"
  10. }
  11. type Timer = ReturnType<typeof setInterval>
  12. type Entry = {
  13. tasks: Map<string, Task>
  14. timers: Map<string, Timer>
  15. }
  16. const create = (): Entry => {
  17. const tasks = new Map<string, Task>()
  18. const timers = new Map<string, Timer>()
  19. return { tasks, timers }
  20. }
  21. const shared = create()
  22. const state = Instance.state(
  23. () => create(),
  24. async (entry) => {
  25. for (const timer of entry.timers.values()) {
  26. clearInterval(timer)
  27. }
  28. entry.tasks.clear()
  29. entry.timers.clear()
  30. },
  31. )
  32. export function register(task: Task) {
  33. const scope = task.scope ?? "instance"
  34. const entry = scope === "global" ? shared : state()
  35. const current = entry.timers.get(task.id)
  36. if (current && scope === "global") return
  37. if (current) clearInterval(current)
  38. entry.tasks.set(task.id, task)
  39. void run(task)
  40. const timer = setInterval(() => {
  41. void run(task)
  42. }, task.interval)
  43. timer.unref()
  44. entry.timers.set(task.id, timer)
  45. }
  46. async function run(task: Task) {
  47. log.info("run", { id: task.id })
  48. await task.run().catch((error) => {
  49. log.error("run failed", { id: task.id, error })
  50. })
  51. }
  52. }