<h2>O Que é Concorrência Real em JavaScript?</h2>
<p>Diferente da concorrência "falsa" criada pelo event loop, concorrência real envolve executar múltiplas operações assíncronas <strong>em paralelo</strong>, 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.</p>
<p>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 <code>Promise.all()</code>, <code>Promise.race()</code>, <code>Promise.allSettled()</code> e <code>Promise.any()</code> — cada uma com seu caso de uso específico.</p>
<h2>Coordenando Múltiplas Promises com Métodos Nativos</h2>
<h3>Promise.all() — Tudo ou Nada</h3>
<p>Use <code>Promise.all()</code> quando <strong>todos</strong> os resultados são críticos. Se uma Promise rejeita, toda a operação falha imediatamente:</p>
<pre><code class="language-javascript">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();</code></pre>
<h3>Promise.allSettled() — Resultados Parciais Aceitáveis</h3>
<p>Quando você precisa dos resultados de <strong>todas</strong> as Promises, mas algumas podem falhar:</p>
<pre><code class="language-javascript">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();</code></pre>
<h3>Promise.race() e Promise.any()</h3>
<p><code>Promise.race()</code> retorna assim que <strong>a primeira</strong> Promise é resolvida ou rejeitada — útil para implementar timeouts. <code>Promise.any()</code> retorna a <strong>primeira Promise que resolve com sucesso</strong>, ignorando rejeições até que todas falhem:</p>
<pre><code class="language-javascript">// 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();</code></pre>
<h2>Padrões Avançados: Controle Fino da Concorrência</h2>
<h3>Executar Promises Sequencialmente com Dependências</h3>
<p>Nem sempre você quer paralelismo total. Quando uma operação depende do resultado da anterior, use <code>async/await</code> com loops:</p>
<pre><code class="language-javascript">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();</code></pre>
<h3>Pool de Concorrência — Limitar Paralelismo</h3>
<p>Para não sobrecarregar a aplicação, controle quantas Promises rodam <strong>simultaneamente</strong>:</p>
<pre><code class="language-javascript">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);
});</code></pre>
<h2>Tratamento de Erros e Debugging</h2>
<p>Erros em Promises precisam de cuidado especial em concorrência. Use <code>.catch()</code> granular ou <code>try/catch</code> com <code>async/await</code>:</p>
<pre><code class="language-javascript">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();</code></pre>
<h2>Conclusão</h2>
<p>Dominar concorrência em JavaScript significa escolher a ferramenta certa para cada cenário: use <code>Promise.all()</code> quando todos os resultados são essenciais, <code>Promise.allSettled()</code> para tolerância a falhas, <code>Promise.race()</code> para race conditions e <code>Promise.any()</code> para redundância. Implemente pools de concorrência para não sobrecarregar recursos e sempre trate erros com precisão. A combinação de <code>async/await</code> 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" target="_blank" rel="noopener noreferrer">MDN Web Docs - Promise.all()</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function" target="_blank" rel="noopener noreferrer">MDN Web Docs - async/await</a></li>
<li><a href="https://javascript.info/promise" target="_blank" rel="noopener noreferrer">JavaScript.info - Promises</a></li>
<li><a href="https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/" target="_blank" rel="noopener noreferrer">Node.js Documentation - Asynchronous Operations</a></li>
<li><a href="https://eloquentjavascript.net/11_async.html" target="_blank" rel="noopener noreferrer">Eloquent JavaScript - Promises</a></li>
</ul>