Trabalhando com filas de tarefas em TypeScript: guia completo
No desenvolvimento web moderno e em aplicações de servidor, surge frequentemente a necessidade de executar tarefas de forma assíncrona, em segundo plano ou com limitação de paralelismo. As filas de tarefas (task queues) são um padrão poderoso que permite organizar a execução previsível e eficiente de operações. Neste artigo, vamos detalhar como criar e usar filas de tarefas em TypeScript, desde implementações simples até técnicas avançadas com prioridades e tratamento de erros.
O que é uma fila de tarefas e para que serve?
Uma fila de tarefas é uma estrutura de dados que funciona pelo princípio FIFO (First In, First Out). As tarefas são adicionadas ao final da fila e retiradas do início. No contexto do TypeScript e Node.js, as filas são usadas para:
- Limitação de paralelismo — controle do número de operações executadas simultaneamente (por exemplo, requisições a APIs com limites).
- Processamento assíncrono — execução de tarefas pesadas em segundo plano sem bloquear a thread principal.
- Tentativas repetidas — reinicialização automática de tarefas que falharam com atraso.
- Gerenciamento de ordem — garantia de execução sequencial de operações dependentes.
TypeScript adiciona tipagem estrita, tornando o código das filas mais confiável e previsível.
Implementação básica de uma fila de tarefas em TypeScript
Vamos começar criando uma fila simples, mas totalmente tipada. Ela aceitará tarefas (funções que retornam Promise) e as executará sequencialmente.
// Definindo o tipo de tarefatype Task<T> = () => Promise<T>;
// Classe da filaclass TaskQueue<T> { private queue: Task<T>[] = []; private isProcessing: boolean = false;
// Adicionando tarefa à fila enqueue(task: Task<T>): void { this.queue.push(task); this.process(); }
// Iniciando o processamento private async process(): Promise<void> { if (this.isProcessing) return; this.isProcessing = true;
while (this.queue.length > 0) { const task = this.queue.shift()!; try { const result = await task(); console.log('Tarefa concluída:', result); } catch (error) { console.error('Erro ao executar tarefa:', error); } }
this.isProcessing = false; }}
// Exemplo de usoconst queue = new TaskQueue<string>();queue.enqueue(async () => { await new Promise(resolve => setTimeout(resolve, 1000)); return 'Primeira tarefa';});queue.enqueue(async () => 'Segunda tarefa (rápida)');Esta implementação garante execução sequencial e início automático do processamento ao adicionar uma nova tarefa. No entanto, ela não suporta paralelismo nem prioridades.
Fila com limitação de paralelismo
Frequentemente, é necessário executar várias tarefas simultaneamente, mas sem exceder um certo limite (por exemplo, 5 requisições paralelas). Vamos adicionar o parâmetro concurrency.
class ParallelTaskQueue<T> { private queue: Task<T>[] = []; private activeCount: number = 0; private readonly concurrency: number;
constructor(concurrency: number = 3) { this.concurrency = concurrency; }
enqueue(task: Task<T>): void { this.queue.push(task); this.processNext(); }
private processNext(): void { while (this.activeCount < this.concurrency && this.queue.length > 0) { const task = this.queue.shift()!; this.activeCount++; task() .then(result => console.log('Sucesso:', result)) .catch(error => console.error('Erro:', error)) .finally((
```