Looping no n8n
Looping é uma técnica fundamental para processar dados repetitivos, iterar sobre arrays e criar workflows dinâmicos. O n8n oferece várias estratégias para implementar loops eficientes e controlados.
Conceitos Fundamentais
O que é Looping?
Looping é o processo de repetir operações sobre um conjunto de dados, permitindo:
- Processamento em lote: Tratar múltiplos itens automaticamente
- Iteração sobre arrays: Processar cada elemento de uma lista
- Retry logic: Tentar operações múltiplas vezes
- Polling: Verificar status periodicamente
- Processamento paralelo: Executar operações simultaneamente
Tipos de Loops
- For Loop: Iteração sobre array com índice
- While Loop: Repetição baseada em condição
- ForEach Loop: Processamento de cada elemento
- Recursive Loop: Chamadas recursivas
- Parallel Loop: Execução simultânea
Implementação
1. Split In Batches Node
O Split In Batches é a forma mais comum de criar loops.
Configuração básica:
{
"batchSize": 10,
"options": {
"reset": false
}
}
Configuração avançada:
{
"batchSize": "{{ $json.items.length > 100 ? 50 : 10 }}",
"options": {
"reset": true,
"waitBetweenBatches": 1000
}
}
2. Code Node para Loops Customizados
Para loops complexos, use o Code Node.
// Loop simples sobre array
const items = $json.items;
const resultados = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
const processado = await processarItem(item);
resultados.push(processado);
}
return resultados;
// Loop com condição
const dados = $json;
const resultados = [];
let i = 0;
while (i < dados.length && dados[i].status !== 'erro') {
const resultado = await processarDado(dados[i]);
resultados.push(resultado);
i++;
}
return resultados;
Padrões de Loop
1. Processamento de Array
Processa cada elemento de um array.
Implementação:
// Dados de entrada
const clientes = [
{ id: 1, nome: 'João', email: 'joao@exemplo.com' },
{ id: 2, nome: 'Maria', email: 'maria@exemplo.com' },
{ id: 3, nome: 'Pedro', email: 'pedro@exemplo.com' }
];
// Processar cada cliente
const clientesProcessados = clientes.map(async cliente => {
// Enriquecer dados
const dadosEnriquecidos = await enriquecerDados(cliente);
// Validar dados
const validacao = validarCliente(dadosEnriquecidos);
return {
...dadosEnriquecidos,
validacao,
processado_em: $now
};
});
return await Promise.all(clientesProcessados);
2. Retry com Backoff
Tenta uma operação múltiplas vezes com delays crescentes.
Implementação:
// Retry com backoff exponencial
const maxTentativas = 5;
const operacao = async () => {
// Simular operação que pode falhar
const response = await fetch('https://api.exemplo.com/dados');
if (!response.ok) {
throw new Error('API não respondeu');
}
return response.json();
};
let tentativa = 0;
let resultado;
while (tentativa < maxTentativas) {
try {
resultado = await operacao();
break; // Sucesso, sair do loop
} catch (error) {
tentativa++;
if (tentativa >= maxTentativas) {
throw new Error(`Falha após ${maxTentativas} tentativas: ${error.message}`);
}
// Backoff exponencial
const delay = Math.pow(2, tentativa) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
return resultado;
3. Polling
Verifica status periodicamente até uma condição ser atendida.
Implementação:
// Polling com timeout
const maxTentativas = 20;
const intervalo = 5000; // 5 segundos
let tentativa = 0;
while (tentativa < maxTentativas) {
const status = await verificarStatus($json.job_id);
if (status.status === 'completed') {
return {
status: 'sucesso',
resultado: status.resultado,
tentativas: tentativa + 1
};
}
if (status.status === 'failed') {
throw new Error(`Job falhou: ${status.erro}`);
}
tentativa++;
if (tentativa < maxTentativas) {
await new Promise(resolve => setTimeout(resolve, intervalo));
}
}
throw new Error(`Timeout: Job não foi concluído em ${maxTentativas * intervalo / 1000} segundos`);
Casos de Uso Práticos
1. Processamento de Pedidos em Lote
Cenário: Processar múltiplos pedidos de uma vez.
Implementação:
// Code Node - Processar lote de pedidos
const pedidos = $json;
const resultados = await Promise.all(
pedidos.map(async pedido => {
try {
// Validar pedido
const validacao = await validarPedido(pedido);
if (!validacao.valido) {
return {
pedido_id: pedido.id,
status: 'invalido',
erro: validacao.erro
};
}
// Processar pagamento
const pagamento = await processarPagamento(pedido);
// Atualizar status
await atualizarStatus(pedido.id, 'pago');
// Enviar confirmação
await enviarConfirmacao(pedido.cliente_email);
return {
pedido_id: pedido.id,
status: 'processado',
pagamento_id: pagamento.id
};
} catch (error) {
return {
pedido_id: pedido.id,
status: 'erro',
erro: error.message
};
}
})
);
return {
total_pedidos: pedidos.length,
sucessos: resultados.filter(r => r.status === 'processado').length,
erros: resultados.filter(r => r.status === 'erro').length,
resultados
};
2. Sincronização de Dados
Cenário: Sincronizar dados entre sistemas diferentes.
Implementação:
// Code Node - Sincronização de dados
const dadosOrigem = $json;
const operacoes = await Promise.all(
dadosOrigem.map(async item => {
try {
// Buscar no sistema destino
const existente = await buscarNoDestino(item.id);
if (!existente) {
// Criar novo
const resultado = await criarNoDestino(item);
return {
id: item.id,
operacao: 'criado',
resultado: resultado.id
};
} else {
// Atualizar existente
const resultado = await atualizarNoDestino(item.id, item);
return {
id: item.id,
operacao: 'atualizado',
resultado: resultado.id
};
}
} catch (error) {
return {
id: item.id,
operacao: 'erro',
erro: error.message
};
}
})
);
return {
total_items: dadosOrigem.length,
criados: operacoes.filter(op => op.operacao === 'criado').length,
atualizados: operacoes.filter(op => op.operacao === 'atualizado').length,
erros: operacoes.filter(op => op.operacao === 'erro').length,
operacoes
};
3. Processamento Paralelo
Cenário: Executar múltiplas operações simultaneamente.
Implementação:
// Code Node - Processamento paralelo
const urls = $json;
// Processar URLs em paralelo
const resultados = await Promise.all(
urls.map(async url => {
try {
const startTime = $now;
const response = await fetch(url);
const endTime = $now;
return {
url,
status: response.status,
tempo_resposta: endTime - startTime,
tamanho: response.headers.get('content-length') || 0
};
} catch (error) {
return {
url,
status: 'erro',
erro: error.message
};
}
})
);
// Calcular estatísticas
const estatisticas = {
total_urls: urls.length,
sucessos: resultados.filter(r => r.status === 200).length,
erros: resultados.filter(r => r.status === 'erro').length,
tempo_medio: resultados
.filter(r => r.tempo_resposta)
.reduce((sum, r) => sum + r.tempo_resposta, 0) /
resultados.filter(r => r.tempo_resposta).length
};
return {
estatisticas,
resultados
};
Expressões e Data Mapping
Loops Dinâmicos
// Tamanho do batch baseado em dados
const batchSize = $json.items.length > 1000 ? 100 : 10;
return {
"batchSize": batchSize
};
Loops Condicionais
// Loop baseado em condição
const dados = $json;
const resultados = [];
for (const item of dados) {
if (item.status === 'pendente') {
const processado = await processarItem(item);
resultados.push(processado);
}
}
return resultados;
Loops com Controle
// Loop com break condicional
const dados = $json;
const resultados = [];
for (const item of dados) {
if (item.prioridade === 'alta') {
const processado = await processarItem(item);
resultados.push(processado);
if (processado.status === 'erro') {
break; // Parar se encontrar erro em item de alta prioridade
}
}
}
return resultados;
Tratamento de Erros
Loop com Error Handling
// Loop com tratamento de erro individual
const dados = $json;
const resultados = [];
const erros = [];
for (const item of dados) {
try {
const resultado = await processarItem(item);
resultados.push({
id: item.id,
status: 'sucesso',
resultado
});
} catch (error) {
erros.push({
id: item.id,
erro: error.message
});
// Continuar processando outros itens
console.error(`Erro processando item ${item.id}:`, error);
}
}
return {
sucessos: resultados,
erros,
total_processados: dados.length
};
Loop com Retry
// Loop com retry para itens que falham
const dados = $json;
const maxTentativas = 3;
const processarComRetry = async (item) => {
for (let tentativa = 1; tentativa <= maxTentativas; tentativa++) {
try {
return await processarItem(item);
} catch (error) {
if (tentativa === maxTentativas) {
throw error;
}
// Aguardar antes da próxima tentativa
await new Promise(resolve => setTimeout(resolve, 1000 * tentativa));
}
}
};
const resultados = await Promise.all(
dados.map(async item => {
try {
const resultado = await processarComRetry(item);
return {
id: item.id,
status: 'sucesso',
resultado
};
} catch (error) {
return {
id: item.id,
status: 'erro',
erro: error.message
};
}
})
);
return resultados;
Performance e Otimização
Boas Práticas
-
Otimize Batch Size
- Use batches apropriados para o volume de dados
- Considere rate limits das APIs
- Balanceie performance e uso de recursos
-
Use Processamento Paralelo
- Execute operações independentes simultaneamente
- Evite loops sequenciais desnecessários
- Monitore uso de recursos
-
Implemente Rate Limiting
- Respeite limites das APIs externas
- Use delays entre operações
- Implemente backoff exponencial
Exemplo de Otimização
// Loop otimizado com rate limiting
const dados = $json;
const resultados = [];
const rateLimit = 10; // operações por segundo
const delay = 1000 / rateLimit;
for (let i = 0; i < dados.length; i++) {
const item = dados[i];
// Processar item
const resultado = await processarItem(item);
resultados.push(resultado);
// Rate limiting
if (i < dados.length - 1) {
await new Promise(resolve => setTimeout(resolve, delay));
}
}
return resultados;
Troubleshooting
Problemas Comuns
Erro: "Timeout"
- Reduza tamanho do batch
- Implemente rate limiting
- Use processamento paralelo
Erro: "Memory limit exceeded"
- Processe dados em batches menores
- Implemente streaming para dados grandes
- Use paginação
Erro: "Rate limit exceeded"
- Implemente delays entre operações
- Use backoff exponencial
- Distribua carga ao longo do tempo
Debugging
// Loop com debugging
const dados = $json;
const resultados = [];
const debug = {
total_items: dados.length,
processados: 0,
erros: 0,
start_time: $now
};
for (const item of dados) {
try {
console.log(`Processando item ${item.id} (${debug.processados + 1}/${dados.length})`);
const resultado = await processarItem(item);
resultados.push(resultado);
debug.processados++;
} catch (error) {
console.error(`Erro processando item ${item.id}:`, error);
debug.erros++;
}
}
debug.end_time = $now;
debug.tempo_total = debug.end_time - debug.start_time;
console.log('Loop Debug:', debug);
return {
resultados,
debug
};
Integração com Outros Nós
Fluxo Típico
Exemplo Completo
// 1. HTTP Request - Buscar dados
{
"url": "https://api.exemplo.com/items",
"method": "GET"
}
// 2. Split In Batches - Dividir em lotes
{
"batchSize": 10
}
// 3. Code Node - Processar lote
const items = $json;
const resultados = [];
for (const item of items) {
const processado = await processarItem(item);
resultados.push(processado);
}
return resultados;
// 4. HTTP Request - Enviar dados processados
{
"url": "https://api.exemplo.com/processados",
"method": "POST",
"body": $json
}
// 5. Code Node - Consolidar resultados
const todosResultados = $input.all();
const consolidado = {
total_processados: todosResultados.length,
sucessos: todosResultados.filter(r => r.status === 'sucesso').length,
erros: todosResultados.filter(r => r.status === 'erro').length,
resultados: todosResultados
};
return consolidado;
Referências
- Split In Batches - Nó específico para loops
- Wait Node - Delays em loops
- Error Handling - Tratamento de erros em loops
- Data Processing - Processamento de dados
Esta documentação está em processo de validação. Os exemplos práticos e configurações de nós apresentados precisam ser testados e validados em ambientes reais. A intenção é sempre fornecer práticas e exemplos que funcionem corretamente em produção. Se encontrar inconsistências ou problemas, por favor, reporte para que possamos melhorar a qualidade da documentação.
Dica: Use loops estrategicamente para processar dados em lote e criar workflows dinâmicos. Lembre-se de sempre implementar rate limiting e tratamento de erros para garantir robustez e performance.