async-runner-js
v1.0.0
Published
Uma classe JavaScript robusta para execução de tarefas assíncronas em paralelo com controle de concorrência, sistema de retry, timeouts e monitoramento de progresso
Downloads
2
Maintainers
Readme
AsyncRunner
Uma classe JavaScript robusta para execução de tarefas assíncronas em paralelo com controle de concorrência, sistema de retry, timeouts e monitoramento de progresso.
📋 Características
- ✅ Execução paralela com controle de concorrência
- ✅ Sistema de retry automático com backoff exponencial
- ✅ Timeout configurável por tarefa
- ✅ Controle de execução (pause/resume/stop)
- ✅ Callbacks para progresso e eventos
- ✅ Relatórios detalhados com estatísticas
- ✅ Gerenciamento de erros robusto
- ✅ Retry seletivo de tarefas falhadas
- ✅ Metadados personalizados por tarefa
- ✅ Status tracking em tempo real
🚀 Instalação
npm install async-runner-js// ESM
import AsyncRunner from 'async-runner-js';
import { createAsyncRunner } from 'async-runner-js';
// CommonJS
const AsyncRunner = require('async-runner-js');
const { createAsyncRunner } = require('async-runner-js');📖 Uso Básico
Exemplo Simples
import AsyncRunner from 'async-runner-js';
// Criar uma instância
const runner = new AsyncRunner({
concurrency: 3, // Máximo 3 tarefas paralelas
retryAttempts: 2, // 2 tentativas por tarefa
timeout: 5000 // 5 segundos de timeout
});
// Adicionar tarefas
runner
.addTask(async () => {
const response = await fetch('https://api.exemplo.com/dados');
return await response.json();
}, { id: 'buscar-dados' })
.addTask(async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
return { resultado: 'processado' };
}, { id: 'processar' });
// Executar
const report = await runner.run();
console.log('Relatório:', report);Exemplo Avançado
const runner = new AsyncRunner({
concurrency: 5,
retryAttempts: 3,
retryDelay: 1000,
timeout: 10000,
onProgress: (progress) => {
console.log(`📊 Progresso: ${progress.percentage}% (${progress.completed}/${progress.total})`);
},
onTaskComplete: (task) => {
console.log(`✅ Tarefa ${task.id} concluída em ${task.endTime - task.startTime}ms`);
},
onTaskError: (task, error) => {
console.log(`❌ Erro na tarefa ${task.id}: ${error.message}`);
}
});
// Adicionar várias tarefas de uma vez
const tarefas = [
[async () => await processarPedido(1), { id: 'pedido-1', priority: 'high' }],
[async () => await processarPedido(2), { id: 'pedido-2', priority: 'medium' }],
[async () => await enviarEmail('[email protected]'), { id: 'email-1' }]
];
runner.addTasks(tarefas);
const report = await runner.run();
// Analisar resultados
console.log(`Total: ${report.summary.total}`);
console.log(`Sucessos: ${report.summary.successful}`);
console.log(`Falhas: ${report.summary.failed}`);
console.log(`Taxa de sucesso: ${report.summary.successRate}%`);⚙️ Configurações
Opções do Constructor
const runner = new AsyncRunner({
concurrency: 3, // Número máximo de tarefas paralelas (padrão: 3)
retryAttempts: 3, // Número de tentativas por tarefa (padrão: 3)
retryDelay: 1000, // Delay entre tentativas em ms (padrão: 1000)
timeout: 30000, // Timeout por tarefa em ms (padrão: 30000)
onProgress: null, // Callback de progresso
onTaskComplete: null, // Callback quando tarefa completa
onTaskError: null // Callback quando tarefa falha
});Callbacks Disponíveis
// Callback de progresso
onProgress: (progress) => {
// progress = { total, completed, pending, percentage }
console.log(`Progresso: ${progress.percentage}%`);
}
// Callback de tarefa completa
onTaskComplete: (task) => {
// task = { id, fn, metadata, status, result, duration, ... }
console.log(`Tarefa ${task.id} concluída`);
}
// Callback de erro
onTaskError: (task, error) => {
console.log(`Erro na tarefa ${task.id}: ${error.message}`);
}📝 API Reference
Métodos Principais
addTask(task, metadata = {})
Adiciona uma única tarefa ao runner.
runner.addTask(
async () => await minhaFuncao(),
{
id: 'minha-tarefa',
priority: 'high',
customData: 'valor personalizado'
}
);addTasks(tasks)
Adiciona múltiplas tarefas de uma vez.
runner.addTasks([
async () => await tarefa1(),
[async () => await tarefa2(), { id: 'tarefa-2' }],
[async () => await tarefa3(), { id: 'tarefa-3', priority: 'low' }]
]);run()
Executa todas as tarefas e retorna um relatório completo.
const report = await runner.run();Controle de Execução
pause() / resume()
Pausa e retoma a execução.
runner.pause(); // Pausa após o lote atual
runner.resume(); // Retoma a execuçãostop()
Para completamente a execução.
runner.stop();Métodos de Consulta
getTaskById(id)
Busca uma tarefa específica pelo ID.
const task = runner.getTaskById('minha-tarefa');
console.log(task.status); // 'pending', 'running', 'completed', 'failed'getTasksByStatus(status)
Busca tarefas por status.
const pendingTasks = runner.getTasksByStatus('pending');
const completedTasks = runner.getTasksByStatus('completed');getFailedTasks() / getSuccessfulTasks()
Busca tarefas falhadas ou bem-sucedidas.
const failed = runner.getFailedTasks();
const successful = runner.getSuccessfulTasks();Retry e Limpeza
retryFailedTasks()
Executa novamente apenas as tarefas que falharam.
if (report.summary.failed > 0) {
console.log('Tentando novamente tarefas falhadas...');
const retryReport = await runner.retryFailedTasks();
}clear() / clearResults()
Limpa tarefas ou apenas resultados.
runner.clear(); // Remove todas as tarefas (apenas se não estiver executando)
runner.clearResults(); // Limpa apenas resultados e errosPropriedades de Status
status
Retorna o status atual do runner.
const status = runner.status;
// { isRunning: false, isPaused: false, progress: {...}, tasksCount: 5 }progress
Retorna o progresso atual.
const progress = runner.progress;
// { total: 10, completed: 7, percentage: 70 }isComplete
Verifica se todas as tarefas foram processadas.
if (runner.isComplete) {
console.log('Todas as tarefas foram processadas!');
}📊 Estrutura do Relatório
O método run() retorna um relatório detalhado:
{
summary: {
total: 10, // Total de tarefas
successful: 8, // Tarefas bem-sucedidas
failed: 2, // Tarefas falhadas
successRate: 80, // Taxa de sucesso em %
totalDuration: "5432ms", // Duração total
averageDuration: "543ms" // Duração média por tarefa
},
results: [
{
taskId: "tarefa-1",
result: { data: "resultado" },
duration: 1200
}
// ...
],
errors: [
{
taskId: "tarefa-2",
error: "Connection timeout",
attempts: 3,
duration: 5000
}
// ...
],
tasks: [
{
id: "tarefa-1",
status: "completed",
attempts: 1,
duration: 1200,
metadata: { priority: "high" }
}
// ...
]
}🔧 Exemplos Práticos
Processamento de Dados em Lote
import AsyncRunner from 'async-runner-js';
async function processarLoteDeUsuarios(usuarios) {
const runner = new AsyncRunner({
concurrency: 10,
retryAttempts: 2,
timeout: 15000,
onProgress: (progress) => {
console.log(`Processando usuários: ${progress.percentage}%`);
}
});
// Adicionar uma tarefa para cada usuário
usuarios.forEach(usuario => {
runner.addTask(
async () => {
// Validar dados
const dadosValidados = await validarUsuario(usuario);
// Salvar no banco
const resultado = await salvarUsuario(dadosValidados);
// Enviar email de boas-vindas
await enviarEmailBoasVindas(usuario.email);
return resultado;
},
{
id: `usuario-${usuario.id}`,
email: usuario.email,
tipo: 'processamento-usuario'
}
);
});
const report = await runner.run();
console.log(`Processamento concluído:`);
console.log(`- Total: ${report.summary.total}`);
console.log(`- Sucessos: ${report.summary.successful}`);
console.log(`- Falhas: ${report.summary.failed}`);
// Retry automático para falhas
if (report.summary.failed > 0) {
console.log('Tentando novamente usuários com falha...');
const retryReport = await runner.retryFailedTasks();
console.log(`Retry: ${retryReport.summary.successful} sucessos adicionais`);
}
return report;
}Download de Arquivos
async function baixarArquivos(urls) {
const runner = new AsyncRunner({
concurrency: 5,
timeout: 30000,
onTaskComplete: (task) => {
console.log(`✅ Download concluído: ${task.metadata.filename}`);
},
onTaskError: (task, error) => {
console.log(`❌ Falha no download: ${task.metadata.filename} - ${error.message}`);
}
});
urls.forEach((url, index) => {
runner.addTask(
async () => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const buffer = await response.arrayBuffer();
const filename = `arquivo_${index + 1}.bin`;
await fs.writeFile(filename, Buffer.from(buffer));
return {
filename,
size: buffer.byteLength,
url
};
},
{
id: `download-${index + 1}`,
filename: `arquivo_${index + 1}.bin`,
url
}
);
});
return await runner.run();
}Integração com APIs
async function sincronizarDados() {
const runner = new AsyncRunner({
concurrency: 3,
retryAttempts: 3,
retryDelay: 2000,
onProgress: (progress) => {
console.log(`Sincronização: ${progress.completed}/${progress.total}`);
}
});
const endpoints = [
{ name: 'usuarios', url: '/api/usuarios' },
{ name: 'produtos', url: '/api/produtos' },
{ name: 'pedidos', url: '/api/pedidos' },
{ name: 'estoque', url: '/api/estoque' }
];
endpoints.forEach(endpoint => {
runner.addTask(
async () => {
// Buscar dados da API
const response = await fetch(`https://api.exemplo.com${endpoint.url}`);
const dados = await response.json();
// Processar e salvar localmente
const resultado = await processarDados(endpoint.name, dados);
return {
endpoint: endpoint.name,
registros: dados.length,
processados: resultado.count
};
},
{
id: `sync-${endpoint.name}`,
endpoint: endpoint.name,
url: endpoint.url
}
);
});
const report = await runner.run();
// Log dos resultados
report.results.forEach(result => {
console.log(`${result.result.endpoint}: ${result.result.processados} registros sincronizados`);
});
return report;
}⚠️ Considerações Importantes
Limitações de Concorrência
- A concorrência é controlada por lotes, não por pool de workers
- Cada lote executa completamente antes do próximo
- Para alta concorrência, considere ajustar o valor adequadamente
Gerenciamento de Memória
- Resultados são mantidos em memória durante a execução
- Para grandes volumes, considere processar em chunks menores
- Use
clearResults()periodicamente se necessário
Tratamento de Erros
- Erros são capturados e não interrompem outras tarefas
- O sistema de retry é automático mas configurável
- Timeouts são tratados como erros e acionam retry
Performance
- O overhead é mínimo para tarefas de longa duração
- Para tarefas muito rápidas, o overhead pode ser significativo
- Teste diferentes configurações de concorrência para seu caso
🐛 Solução de Problemas
Tarefas Não Executam
// Verifique se são funções assíncronas
runner.addTask(async () => {
// Sua lógica aqui
});
// Não faça isso:
runner.addTask(() => {
// Função síncrona pode não funcionar como esperado
});Timeouts Frequentes
// Aumente o timeout para tarefas demoradas
const runner = new AsyncRunner({
timeout: 60000 // 1 minuto
});Muitas Falhas
// Ajuste o número de tentativas e delay
const runner = new AsyncRunner({
retryAttempts: 5,
retryDelay: 2000 // 2 segundos entre tentativas
});Problemas de Memória
// Processe em lotes menores
const runner = new AsyncRunner({
concurrency: 2 // Reduza a concorrência
});
// Limpe resultados periodicamente
runner.clearResults();📄 Licença
Este projeto está sob a licença MIT. Veja o arquivo LICENSE para mais detalhes.
🤝 Contribuição
Contribuições são bem-vindas! Por favor, abra uma issue ou pull request para melhorias.
AsyncRunner - Execução assíncrona simplificada e poderosa para JavaScript/Node.js
