Promise.all, allSettled e race em JavaScript
Quatro formas de combinar Promises e o que muda entre elas: fail-fast, relatório completo, primeira resposta — com o tempo medido em requisições de verdade.
Promise.all recebe uma lista de Promises e devolve uma Promise só, que resolve
com o array de todos os resultados. É a ferramenta para disparar várias
operações independentes ao mesmo tempo e esperar uma vez em vez de várias.
Ao lado dela existem outras três, e a escolha entre elas é sempre a mesma
pergunta: o que deve acontecer quando uma das operações falha? all desiste
de tudo, allSettled entrega o relatório completo, race pega a primeira que
terminar e any pega a primeira que der certo.
Os exemplos rodam contra a API local da loja apresentada em como consumir uma API com fetch.
Uma comanda com vários pedidos independentes
Imagine uma mesa que pediu café, suco e pão na chapa. Se a cozinha esperar o café ficar pronto para começar o suco, a mesa paga pela soma de todos os tempos. Quando os três pedidos são independentes, eles podem começar juntos; a comanda só é entregue quando a regra escolhida disser que terminou.
Promise.all é o combinador que espera todas e falha na primeira rejeição.
allSettled, race e any mantêm o início paralelo, mas mudam a regra de
encerramento. Na primeira medição, some os atrasos individuais e depois compare
com o tempo total real: a diferença verifica que as operações foram disparadas
em paralelo, não uma depois da outra.
Três chamadas independentes, com 400ms, 300ms e 500ms de resposta. Uma de cada vez:
const inicio = Date.now();
const produtos = await fetch('http://localhost:4599/produtos?ms=400').then((r) => r.json());
const estoque = await fetch('http://localhost:4599/estoque?id=1&ms=300').then((r) => r.json());
const frete = await fetch('http://localhost:4599/frete?cep=01310100&ms=500').then((r) => r.json());
console.log(produtos.length, estoque.disponivel, frete.valor);
console.log('em série:', Date.now() - inicio, 'ms');Agora as mesmas três, disparadas juntas:
const inicio = Date.now();
const [produtos, estoque, frete] = await Promise.all([
fetch('http://localhost:4599/produtos?ms=400').then((r) => r.json()),
fetch('http://localhost:4599/estoque?id=1&ms=300').then((r) => r.json()),
fetch('http://localhost:4599/frete?cep=01310100&ms=500').then((r) => r.json()),
]);
console.log(produtos.length, estoque.disponivel, frete.valor);
console.log('em paralelo:', Date.now() - inicio, 'ms');1236ms contra 523ms. O tempo em série é a soma; em paralelo é o maior, mais o custo de abrir as conexões. Numa tela que carrega seis blocos de dados, essa diferença é a distância entre um carregamento instantâneo e um usuário olhando para um spinner.
A ordem do array é a de entrada, não a de chegada
Isso é garantido pela especificação e é o que permite desestruturar o resultado com segurança:
const espera = (ms, valor) =>
new Promise((resolve) =>
setTimeout(() => {
console.log(`terminou: ${valor} (${ms}ms)`);
resolve(valor);
}, ms),
);
const resultados = await Promise.all([
espera(500, 'frete'),
espera(100, 'estoque'),
espera(300, 'cupom'),
]);
console.log('array final:', resultados);Chegaram em uma ordem, saíram na outra. É por isso que
const [cliente, frete] = await Promise.all([...]) nunca troca as variáveis de
lugar, por mais instável que seja a rede.
all é fail-fast — e não cancela nada
Uma rejeição derruba a chamada inteira, na hora. Mas as outras Promises não param: elas continuam rodando, invisíveis, e terminam depois.
const espera = (ms, valor) =>
new Promise((resolve) =>
setTimeout(() => {
console.log(` terminou mesmo assim: ${valor}`);
resolve(valor);
}, ms),
);
const falhar = (ms, motivo) =>
new Promise((_, reject) => setTimeout(() => reject(new Error(motivo)), ms));
const inicio = Date.now();
try {
await Promise.all([
espera(500, 'frete'),
falhar(100, 'transportadora fora do ar'),
espera(300, 'cupom'),
]);
} catch (e) {
console.log(`Promise.all rejeitou aos ${Date.now() - inicio}ms:`, e.message);
}
await espera(600, 'fim da observação');Duas leituras. Primeira: o catch recebeu um erro — o primeiro. Se três
falharem, você fica sabendo de uma. Segunda: cupom e frete terminaram depois
da rejeição, gastando conexão e banda de quem já desistiu. Para interromper de
verdade é preciso um AbortController compartilhado entre as requisições.
allSettled: o relatório de todas
Nunca rejeita. Devolve um array de objetos com status, e value ou reason.
Num arquivo novo, com as duas ajudantes silenciosas — sem o console.log que a
seção anterior usou para espiar o fail-fast:
const espera = (ms, valor) => new Promise((resolve) => setTimeout(() => resolve(valor), ms));
const falhar = (ms, motivo) =>
new Promise((_, reject) => setTimeout(() => reject(new Error(motivo)), ms));
const resultados = await Promise.allSettled([
espera(500, { servico: 'frete', valor: 24.9 }),
falhar(100, 'transportadora fora do ar'),
espera(300, { servico: 'cupom', desconto: 10 }),
]);
console.log(resultados);
const oks = resultados.filter((r) => r.status === 'fulfilled').map((r) => r.value);
const falhas = resultados.filter((r) => r.status === 'rejected').map((r) => r.reason.message);
console.log('deu certo:', oks.length, '| falhou:', falhas);O preço é ter que abrir cada item para saber o que veio — não dá para desestruturar direto. Em troca, você recebe o motivo de cada falha, e não só o primeiro.
race e any: quem chegar primeiro
race entrega o primeiro resultado, seja ele qual for — inclusive uma rejeição:
const vencedor = await Promise.race([espera(500, 'transportadora A'), espera(200, 'transportadora B')]);
console.log('cotação mais rápida:', vencedor);
try {
await Promise.race([espera(500, 'transportadora A'), falhar(100, 'transportadora B caiu')]);
} catch (e) {
console.log('race também perde para uma rejeição:', e.message);
}any ignora as rejeições e espera a primeira que der certo. Se todas falharem,
ele rejeita com um AggregateError que carrega a lista de motivos:
const primeira = await Promise.any([
falhar(100, 'transportadora A caiu'),
espera(300, 'transportadora B: R$ 24,90'),
espera(500, 'transportadora C: R$ 19,90'),
]);
console.log('primeira que respondeu:', primeira);
try {
await Promise.any([falhar(100, 'A caiu'), falhar(200, 'B caiu')]);
} catch (e) {
console.log('nome:', e.name);
console.log('mensagem:', e.message);
console.log('motivos:', e.errors.map((x) => x.message));
}O e.errors é exclusivo do AggregateError e é o que salva o diagnóstico: sem
ele, “todas falharam” não diz por quê. Um uso honesto de race é impor
tempo-limite a uma operação que não tem cancelamento próprio; para requisições
HTTP, prefira AbortSignal.timeout.
Dez requisições, uma falha
O cenário que aparece em produção: uma listagem carrega dez produtos e um dos ids não existe mais.
const ids = [1, 2, 3, 99, 1, 2, 3, 1, 2, 3];
const buscar = async (id) => {
const r = await fetch(`http://localhost:4599/produtos/${id}`);
if (!r.ok) throw new Error(`produto ${id}: HTTP ${r.status}`);
return r.json();
};
const produtos = await Promise.all(ids.map(buscar));
console.log('carregou', produtos.length, 'produtos');Nove produtos vieram e nenhum foi aproveitado: a tela ficou vazia por causa de
um. E repare no presente que o stack trace dá — at async Promise.all (index 3)
diz exatamente qual posição do array rejeitou. É a informação que economiza
meia hora de log.
Trocando uma palavra, a listagem passa a mostrar o que deu certo:
const resultados = await Promise.allSettled(ids.map(buscar));
const carregados = resultados.filter((r) => r.status === 'fulfilled');
const quebrados = resultados.filter((r) => r.status === 'rejected');
console.log('carregou', carregados.length, 'de', ids.length);
for (const r of quebrados) console.log(' falhou:', r.reason.message);Qual usar em cada caso
| método | resolve quando | rejeita quando | resultado |
|---|---|---|---|
all |
todas resolvem | a primeira rejeitar | array de valores, na ordem de entrada |
allSettled |
todas terminam | nunca | array de {status, value/reason} |
race |
a primeira terminar | a primeira terminar for rejeição | o valor ou o erro de quem chegou antes |
any |
a primeira resolver | todas rejeitarem | o valor, ou AggregateError com errors |
Um último cuidado que a tabela não mostra: Promise.all não tem limite de
concorrência. Mil ids em map viram mil requisições simultâneas, e o servidor
provavelmente vai responder com 429 ou simplesmente derrubar as conexões. Para
lotes grandes, fatie o array em blocos de dez ou vinte e chame all por bloco —
ou use uma biblioteca de fila com limite.
Com isso fecha o bloco assíncrono da trilha de JavaScript:
event loop, timers, Promise,
async/await, fetch e combinadores. O
guia de JavaScript mostra o que vem depois — e, se o
próximo projeto envolve chamar uma API de modelo de linguagem, os combinadores
desta lição são exatamente o que segura várias chamadas caras em paralelo; o
contexto está em o que é um LLM.
Perguntas frequentes
Promise.all cancela as outras requisições quando uma falha?
A ordem do array de resultados é a ordem de chegada?
Quando usar allSettled em vez de all?
Qual a diferença entre race e any?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — Promise.all() — developer.mozilla.org
- MDN — Promise.allSettled() — developer.mozilla.org


