O Que é Concorrência Real em JavaScript?
Diferente da concorrência "falsa" criada pelo event loop, concorrência real envolve executar múltiplas operações assíncronas em paralelo, coordenando seus resultados de forma eficiente. Em JavaScript, isso é realizado através de Promises, que permitem trabalhar com operações não-bloqueantes como requisições HTTP, leitura de arquivos e acesso a bancos de dados.
Uma Promise representa um valor que pode estar disponível agora, no futuro ou nunca. O grande desafio é coordenar várias Promises simultaneamente, esperando que todas sejam resolvidas ou tratando falhas parciais. Para dominar concorrência real, você precisa entender quando usar Promise.all(), Promise.race(), Promise.allSettled() e Promise.any() — cada uma com seu caso de uso específico.
Coordenando Múltiplas Promises com Métodos Nativos
Promise.all() — Tudo ou Nada
Use Promise.all() quando todos os resultados são críticos. Se uma Promise rejeita, toda a operação falha imediatamente:
const fetchUserData = async () => {
const urls = [
'https://api.github.com/users/torvalds',
'https://api.github.com/users/gvanrossum',
'https://api.github.com/users/brendaneich'
];
try {
const responses = await Promise.all(urls.map(url => fetch(url)));
const data = await Promise.all(responses.map(r => r.json()));
console.log('Todos os dados carregados:', data);
} catch (error) {
console.error('Erro ao buscar dados:', error.message);
}
};
fetchUserData();
Promise.allSettled() — Resultados Parciais Aceitáveis
Quando você precisa dos resultados de todas as Promises, mas algumas podem falhar:
const processMultipleFiles = async () => {
const filePromises = [
fetch('/api/file1').then(r => r.json()),
fetch('/api/file2').then(r => r.json()),
fetch('/api/file3').then(r => r.json())
];
const results = await Promise.allSettled(filePromises);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Arquivo ${index + 1}: Sucesso -`, result.value);
} else {
console.log(`Arquivo ${index + 1}: Erro -`, result.reason.message);
}
});
};
processMultipleFiles();
Promise.race() e Promise.any()
Promise.race() retorna assim que a primeira Promise é resolvida ou rejeitada — útil para implementar timeouts. Promise.any() retorna a primeira Promise que resolve com sucesso, ignorando rejeições até que todas falhem:
// Implementando timeout com race()
const fetchWithTimeout = (url, timeoutMs) => {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeoutMs)
)
]);
};
// Promise.any() — ignore falhas parciais
const tryMultipleServers = async () => {
const servers = [
fetch('https://server1.com/api'),
fetch('https://server2.com/api'),
fetch('https://server3.com/api')
];
try {
const firstSuccess = await Promise.any(servers);
const data = await firstSuccess.json();
console.log('Primeiro servidor respondeu:', data);
} catch (error) {
console.error('Todos os servidores falharam');
}
};
tryMultipleServers();
Padrões Avançados: Controle Fino da Concorrência
Executar Promises Sequencialmente com Dependências
Nem sempre você quer paralelismo total. Quando uma operação depende do resultado da anterior, use async/await com loops:
const fetchRelatedData = async () => {
try {
// Busca usuário
const userResponse = await fetch('/api/user/123');
const user = await userResponse.json();
console.log('Usuário:', user.name);
// Busca posts do usuário (depende do ID)
const postsResponse = await fetch(`/api/users/${user.id}/posts`);
const posts = await postsResponse.json();
console.log('Posts:', posts.length);
// Busca comentários (depende dos IDs dos posts)
const comments = await Promise.all(
posts.map(post => fetch(`/api/posts/${post.id}/comments`).then(r => r.json()))
);
console.log('Total de comentários:', comments.flat().length);
} catch (error) {
console.error('Erro na cadeia de requisições:', error);
}
};
fetchRelatedData();
Pool de Concorrência — Limitar Paralelismo
Para não sobrecarregar a aplicação, controle quantas Promises rodam simultaneamente:
const concurrencyPool = async (promises, poolSize) => {
const results = [];
const executing = [];
for (let promise of promises) {
const exec = Promise.resolve(promise).then(
(result) => {
executing.splice(executing.indexOf(exec), 1);
return result;
}
);
results.push(exec);
executing.push(exec);
if (executing.length >= poolSize) {
await Promise.race(executing);
}
}
return Promise.all(results);
};
// Uso: processar 100 requisições com máximo de 5 simultâneas
const urls = Array.from({ length: 100 }, (_, i) => `https://api.example.com/item/${i}`);
const promises = urls.map(url => fetch(url).then(r => r.json()));
concurrencyPool(promises, 5).then(results => {
console.log('Todas as requisições completadas:', results.length);
});
Tratamento de Erros e Debugging
Erros em Promises precisam de cuidado especial em concorrência. Use .catch() granular ou try/catch com async/await:
const robustFetching = async () => {
const tasks = [
fetch('/api/critical').catch(e => {
console.error('Critical endpoint falhou:', e);
throw e; // Re-lança para Promise.all detectar
}),
fetch('/api/optional').catch(e => {
console.warn('Optional endpoint falhou, continuando com valor padrão');
return { data: null }; // Recuperação graciosa
})
];
try {
const [critical, optional] = await Promise.all(tasks);
const criticalData = await critical.json();
const optionalData = await optional.json();
console.log('Processamento bem-sucedido:', { criticalData, optionalData });
} catch (error) {
console.error('Falha fatal na operação concorrente:', error);
}
};
robustFetching();
Conclusão
Dominar concorrência em JavaScript significa escolher a ferramenta certa para cada cenário: use Promise.all() quando todos os resultados são essenciais, Promise.allSettled() para tolerância a falhas, Promise.race() para race conditions e Promise.any() para redundância. Implemente pools de concorrência para não sobrecarregar recursos e sempre trate erros com precisão. A combinação de async/await com esses métodos nativos oferece controle fino sobre operações paralelas, transformando código assíncrono complexo em lógica clara e performática.