markdown-worker-queue.ts 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. export function createLatestWorkerQueue<T extends { key: string }>(input: {
  2. run: (request: T) => Promise<void>
  3. supersede: (request: T) => void
  4. dispose: (key: string) => void
  5. }) {
  6. type Slot = { type: "highlight"; key: string; request?: T }
  7. const jobs: Array<Slot | { type: "dispose"; key: string }> = []
  8. const slots = new Map<string, Slot>()
  9. let running: Promise<void> | undefined
  10. let cursor = 0
  11. const schedule = () => {
  12. if (running) return
  13. running = Promise.resolve()
  14. .then(async () => {
  15. while (cursor < jobs.length) {
  16. const job = jobs[cursor++]!
  17. if (job.type === "dispose") {
  18. input.dispose(job.key)
  19. continue
  20. }
  21. if (slots.get(job.key) === job) slots.delete(job.key)
  22. const request = job.request
  23. job.request = undefined
  24. if (request) await input.run(request)
  25. }
  26. })
  27. .finally(() => {
  28. jobs.splice(0, cursor)
  29. cursor = 0
  30. running = undefined
  31. if (jobs.length > 0) schedule()
  32. })
  33. }
  34. return {
  35. highlight(request: T) {
  36. const slot = slots.get(request.key)
  37. if (slot) {
  38. if (slot.request) input.supersede(slot.request)
  39. slot.request = request
  40. return
  41. }
  42. const next: Slot = { type: "highlight", key: request.key, request }
  43. slots.set(request.key, next)
  44. jobs.push(next)
  45. schedule()
  46. },
  47. dispose(key: string) {
  48. const slot = slots.get(key)
  49. if (slot?.request) input.supersede(slot.request)
  50. if (slot) {
  51. slot.request = undefined
  52. slots.delete(key)
  53. }
  54. jobs.push({ type: "dispose", key })
  55. schedule()
  56. },
  57. pending: () => slots.size,
  58. async idle() {
  59. while (running) await running
  60. },
  61. }
  62. }