| 9 | } & QueueAddOptions; |
| 10 | |
| 11 | export default class PriorityQueue implements Queue<RunFunction, PriorityQueueOptions> { |
| 12 | readonly #queue: Array<PriorityQueueOptions & {run: RunFunction}> = []; |
| 13 | |
| 14 | // The queue is stored as a sorted array, but dequeued items are left before `#head` until compaction. Only items from `#head` onward are live, which keeps repeated dequeues amortized O(1). |
| 15 | #head = 0; |
| 16 | |
| 17 | enqueue(run: RunFunction, options?: Partial<PriorityQueueOptions>): void { |
| 18 | const { |
| 19 | priority = 0, |
| 20 | id, |
| 21 | } = options ?? {}; |
| 22 | |
| 23 | const {size} = this; |
| 24 | const element = { |
| 25 | priority, |
| 26 | id, |
| 27 | run, |
| 28 | }; |
| 29 | |
| 30 | if (size === 0) { |
| 31 | // When the queue is logically empty, discard any consumed prefix before accepting new work. |
| 32 | this.#queue.length = 0; |
| 33 | this.#head = 0; |
| 34 | this.#queue.push(element); |
| 35 | return; |
| 36 | } |
| 37 | |
| 38 | if (this.#queue.at(-1)!.priority! >= priority) { |
| 39 | // Same-priority and lower-priority items belong after the current tail. Appending preserves FIFO order for equal priorities. |
| 40 | this.#queue.push(element); |
| 41 | return; |
| 42 | } |
| 43 | |
| 44 | // Binary insertion must run on the live sorted range only. |
| 45 | this.#compact(); |
| 46 | const index = lowerBound(this.#queue, element, (a: Readonly<PriorityQueueOptions>, b: Readonly<PriorityQueueOptions>) => b.priority! - a.priority!); |
| 47 | this.#queue.splice(index, 0, element); |
| 48 | } |
| 49 | |
| 50 | setPriority(id: string, priority: number) { |
| 51 | // A dequeued item with the same id is no longer part of the queue. |
| 52 | const index = this.#queue.findIndex((element: Readonly<PriorityQueueOptions>, index) => index >= this.#head && element.id === id); |
| 53 | if (index === -1) { |
| 54 | throw new ReferenceError(`No promise function with the id "${id}" exists in the queue.`); |
| 55 | } |
| 56 | |
| 57 | const [item] = this.#queue.splice(index, 1); |
| 58 | this.enqueue(item!.run, {priority, id}); |
| 59 | } |
| 60 | |
| 61 | remove(id: string): void; |
| 62 | remove(run: RunFunction): void; |
| 63 | remove(idOrRun: string | RunFunction): void { |
| 64 | const index = this.#queue.findIndex((element: Readonly<PriorityQueueOptions & {run: RunFunction}>, index) => { |
| 65 | // The consumed prefix may still contain references that should not be removable. |
| 66 | if (index < this.#head) { |
| 67 | return false; |
| 68 | } |
nothing calls this directly
no outgoing calls
no test coverage detected