Reimplementação em TypeScript da fila leve yocto-queue.
Quando uma fila começa como um array simples, logo aparecem vários shift(). Em scripts pequenos isso passa, mas dentro de limitadores de concorrência e agendadores de tarefas vale separar essa responsabilidade. yocto-queue é esse formato mínimo e fica fácil copiar para o projeto quando eu quero o comportamento sem adicionar outra dependência.
/** * @see {@link https://github.com/sindresorhus/yocto-queue/blob/main/index.js} */
interface QNode<T> { value: T; next: QNode<T> | undefined;}
interface Queue<T> { enqueue(value: T): void; dequeue(): T | undefined; peek(): T | undefined; clear(): void; readonly size: number; [Symbol.iterator](): Generator<T, void, unknown>; drain(): Generator<T | undefined, void, unknown>;}
function createNode<T>(value: T): QNode<T> { return { value, next: undefined };}
function createQueue<T>(): Queue<T> { let head: QNode<T> | undefined; let tail: QNode<T> | undefined; let size: number;
const clear = (): void => { head = tail = undefined; size = 0; }
clear();
const enqueue = (value: T): void => { const node = createNode(value); if (head) { tail && (tail.next = node); tail = node; } else { head = node; tail = node; }
size++; }
const dequeue = (): T | undefined => { const current = head; if (!current) return; head = head?.next; size--; return current.value; }
const peek = (): T | undefined => { return head?.value; }
function* iterator(): Generator<T, void, unknown> { let current = head; while (current) { yield current.value; current = current.next; } }
function* drain(): Generator<T | undefined, void, unknown> { while (head) { yield dequeue(); } }
return { enqueue, dequeue, peek, clear, get size() { return size; }, [Symbol.iterator]: iterator, drain }}Onde isso encaixa bem
Funciona bem em controle de concorrência com Promises, busca em largura e filas de espera antes de despachar trabalho para workers. Dá para fazer o mesmo com arrays, mas extrair a fila como estrutura própria deixa a intenção do código mais clara.
Por que esse formato funciona
Como a implementação mantém head e tail separados, enqueue() e dequeue() não dependem de reindexar um array inteiro. O getter size também deixa visível a quantidade pendente, enquanto iterator e drain() separam leitura de consumo destrutivo.
Observações
Esta é uma fila FIFO mínima de propósito. Ela não tenta cobrir prioridade nem acesso aleatório. Se tudo o que eu preciso é uma fila simples, isso basta. Se também preciso de cancelamento, prioridade ou deduplicação, outra estrutura faz mais sentido.
hsb.horse