Promessas de JavaScript explicadas: então, catch, Promise.all e erros comuns
As promessas são como o JavaScript lida com o trabalho que termina mais tarde, e o async/await é construído sobre elas. Aprenda como eles realmente se comportam, com resultados reais de cada exemplo, e evite os erros que engolem seus erros.
- Uma promessa é um objeto que representa um resultado que estará disponível posteriormente.
- Fica pendente até ser cumprido ou rejeitado e, uma vez liquidado, nunca muda.
- Cada
thenretorna uma nova promessa, que é o que torna possível o encadeamento. - Use
Promise.allquando todos os resultados forem necessários ePromise.allSettledquando desejar todos os resultados.
Solicitações de rede, leituras de arquivos e temporizadores terminam mais tarde, mas o JavaScript não pode simplesmente parar e esperar, ou a página congelaria. As promessas do JavaScript oferecem uma maneira organizada de dizer “quando isso terminar, faça aquilo e, se falhar, faça isso”.
Mesmo que você escreva principalmente async e await, você estará usando promessas subjacentes, portanto, entendê-las torna a depuração muito mais fácil. Executamos esses exemplos no Node.js 24 e a saída mostrada vem dessa execução. Eles se comportam da mesma maneira nos navegadores modernos.
O que é uma promessa?
Uma promessa é um espaço reservado para um valor futuro. Pense na campainha que um restaurante lhe dá quando você faz um pedido. Você ainda não tem sua comida, mas segura algo que lhe dirá quando estiver pronta ou que há um problema, e enquanto isso você está livre para fazer outras coisas.
No código, uma função assíncrona retorna uma promessa imediatamente e você anexa o que deve acontecer a seguir:
fetch("/api/user")
.then((response) => response.json())
.then((user) => console.log(user.name))
.catch((error) => console.log("Failed:", error.message));
Os três estados prometidos
| Estado | Significado | O que funciona |
|---|---|---|
| Pendente | O trabalho não terminou | Nada ainda |
| Cumprido | Terminou com um valor | then manipuladores |
| Rejeitado | Falhou por um motivo | catch manipuladores |
Uma promessa que é cumprida ou rejeitada é chamada de resolvida. Uma promessa só pode ser cumprida uma vez. Depois disso, seu estado e valor são fixos e as chamadas posteriores para resolve ou reject são ignoradas.
Crie uma promessa
Você normalmente consome promessas de APIs como fetch, mas criar uma mostra como elas funcionam. O construtor recebe duas funções, resolve e reject:
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: "Asha" });
} else {
reject(new Error("Invalid id"));
}
}, 100);
});
}
console.log(fetchUser(1));
// Promise { <pending> }
Registrar a promessa imediatamente mostra que ela ainda está pendente, porque o cronômetro não foi acionado. O valor só pode ser alcançado através de um manipulador ou await. Sempre rejeite com um objeto Error em vez de uma string, pois um Error carrega um rastreamento de pilha que ajuda na depuração.
então, pegue e finalmente
Esses três métodos vinculam manipuladores a uma promessa:
fetchUser(1).then((user) => console.log(user.name));
// Asha
fetchUser(-1)
.then((user) => console.log("never runs", user))
.catch((error) => console.log("caught:", error.message))
.finally(() => console.log("finally"));
// caught: Invalid id
// finally
then é executado em caso de sucesso, catch é executado em caso de falha e finally é executado de qualquer maneira. Isso torna finally o lugar certo para limpeza, como ocultar um botão giratório de carregamento. Não recebe argumentos, porque não deveria se preocupar com o resultado.
Encadeando promessas
A ideia principal é que then sempre retorna uma nova promessa. Qualquer que seja o retorno do seu manipulador, torna-se o valor dessa nova promessa:
Promise.resolve(2)
.then((n) => n * 10)
.then((n) => n + 1)
.then((n) => console.log("chain:", n));
// chain: 21
Se um manipulador retornar outra promessa, a cadeia espera que ela seja resolvida antes de prosseguir. Isto é o que transforma retornos de chamada aninhados em uma sequência simples de etapas:
getUser(id)
.then((user) => getOrders(user.id))
.then((orders) => getInvoice(orders[0].id))
.then((invoice) => console.log(invoice.total))
.catch(handleError);
Como os erros viajam através de uma cadeia
Uma promessa rejeitada pula cada then até chegar a catch. Jogar dentro de um manipulador também conta como rejeição. E um catch que retorna normalmente coloca a cadeia de volta nos trilhos:
Promise.resolve("start")
.then(() => {
throw new Error("step 2 failed");
})
.then(() => console.log("skipped"))
.catch((error) => {
console.log("caught:", error.message);
return "recovered";
})
.then((value) => console.log("continues:", value));
// caught: step 2 failed
// continues: recovered
Pense nisso como a versão assíncrona de try e catch. Um catch no final geralmente cobre toda a cadeia. Se você deseja apenas registrar um erro e ainda assim falhar, lance-o novamente de dentro do catch.
Dica: fetch rejeita apenas falhas de rede. Uma resposta 404 ou 500 ainda é válida, então verifique response.ok você mesmo. Nossa folha de referências de códigos de status HTTP explica o que cada código significa.
Promise.all, allSettled, raça e qualquer
Esses métodos estáticos coordenam várias promessas ao mesmo tempo. Todos eles pegam um array e retornam uma promessa.
| Método | Cumpre quando | Rejeita quando | Use-o para |
|---|---|---|---|
Promise.all |
Todos cumprem | Qualquer um rejeita | Todos os resultados são obrigatórios |
Promise.allSettled |
Todos se acomodam | Nunca | Você quer todos os resultados |
Promise.race |
O primeiro a resolver cumpre | O primeiro a resolver rejeitos | Tempos limite |
Promise.any |
O primeiro cumpre | Todos rejeitam | A primeira fonte de sucesso |
Para testá-los, usamos dois pequenos ajudantes, um que funciona após um atraso e outro que falha após um atraso:
const wait = (ms, value) =>
new Promise((resolve) => setTimeout(() => resolve(value), ms));
const fail = (ms, message) =>
new Promise((_, reject) => setTimeout(() => reject(new Error(message)), ms));
Promise.all
await Promise.all([wait(300, "a"), wait(100, "b"), wait(200, "c")]);
// [ 'a', 'b', 'c' ] took about 300 ms
Dois detalhes a serem observados. Os resultados mantêm a ordem de entrada, mesmo que b tenha terminado primeiro. E o tempo total foi de cerca de 300 ms, o tempo da tarefa mais lenta e não a soma dos 600 ms, pois as três já estavam rodando ao mesmo tempo. Se algum deles rejeita, a coisa toda rejeita imediatamente:
await Promise.all([wait(100, "a"), fail(50, "b failed")]);
// rejected: b failed
Promise.allSettled
Este espera por tudo e reporta cada resultado, para que uma falha não perca o resto. É adequado para um painel onde cada widget é carregado de forma independente:
const results = await Promise.allSettled([wait(50, "a"), fail(50, "b failed")]);
results.map((r) => r.status);
// [ 'fulfilled', 'rejected' ]
Promessa.corrida e Promessa.qualquer
await Promise.race([wait(300, "slow"), wait(100, "fast")]); // fast
await Promise.any([fail(50, "x"), wait(100, "ok")]); // ok
A diferença é que race é o primeiro a resolver, mesmo que tenha falhado, enquanto any ignora as falhas desde que algo dê certo. Um uso clássico de race é adicionar um tempo limite a uma solicitação lenta, embora a tarefa mais lenta continue sendo executada em segundo plano, a menos que você a cancele.
Promessas e assíncrono/espera
async e await são uma forma mais legível de trabalhar com as mesmas promessas. Uma função async sempre retorna uma promessa e await pausa essa função até que uma promessa seja estabelecida:
async function showInvoice(id) {
try {
const user = await getUser(id);
const orders = await getOrders(user.id);
console.log(orders.length);
} catch (error) {
console.log("Failed:", error.message);
}
}
As rejeições tornam-se erros lançados, então try e catch comuns funcionam. Você ainda precisa de Promise.all para trabalho paralelo, porque aguardar uma chamada após a outra as executa em sequência. Nosso guia JavaScript assíncrono/aguardado se aprofunda nisso.
Promessas e o ciclo de eventos
Os manipuladores de promessa nunca são chamados imediatamente, mesmo quando a promessa já foi cumprida. Eles são executados após o término do código atual e antes de qualquer temporizador. Aqui está a prova:
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("microtask"));
console.log("sync");
// sync
// microtask
// timeout
O código síncrono é executado primeiro. Em seguida vem a fila de microtarefas, que contém manipuladores de promessas. Só então o loop de eventos seleciona a próxima tarefa, como um cronômetro. Esta é uma pergunta popular em entrevistas e explica por que um setTimeout igual a zero não é realmente imediato.
Promessas versus retornos de chamada
| Preocupação | Retornos de chamada | Promessas |
|---|---|---|
| Sequenciamento | Aninhado, mais profundo em cada etapa | Uma corrente plana |
| Erros | Verificado em todos os níveis | Um catch pode cobrir a corrente |
| Trabalho paralelo | Contadores manuais | Promise.all |
| Ligou duas vezes por engano? | Possível | Não é possível resolver duas vezes |
Erros comuns
- Esquecendo de retornar para dentro de
then. Sem umreturn, o próximo passo recebeundefinede não espera pela sua promessa interior. - Não
catch. Isso causa uma rejeição não tratada e, no Node.js, isso interrompe o processo por padrão. - Aninhamento em vez de encadeamento. Colocar
thendentro dethenrecria o inferno de retorno de chamada. Devolva a promessa e continue a corrente. - Envolvendo uma promessa em
new Promise. Se uma função já retorna uma promessa, basta usá-la. - Usando
forEachcom retornos de chamada assíncronos. Não espera. Usefor...ofouPromise.allcommap, conforme mostrado em nosso guia para métodos de array JavaScript. - Pensar que uma promessa pode ser cancelada. Não pode por si só. Para solicitações, use
AbortController.
Considerações finais
As promessas se resumem a algumas regras: uma promessa é estabelecida uma vez, then retorna uma nova promessa e os erros percorrem a cadeia até que algo os detecte. Uma vez que essas regras pareçam naturais, async e await deixam de ser mágicas. Para praticar, reescreva uma função baseada em retorno de chamada usando promessas, depois busque dois pontos de extremidade com Promise.all e trate uma falha adequadamente.
Perguntas frequentes
O que é uma promessa em JavaScript?
Uma promessa é um objeto que representa o resultado final do trabalho assíncrono. Começa como pendente e depois cumpre com um valor ou rejeita com um motivo.
Qual é a diferença entre then e catch?
então é executado quando uma promessa é cumprida e o catch é executado quando é rejeitado. catch é efetivamente apenas com um manipulador de rejeição e também pode capturar erros lançados em etapas anteriores.
Qual é a diferença entre Promise.all e Promise.allSettled?
Promise.all rejeita assim que uma promessa é rejeitada. Promise.allSettled sempre espera por todos eles e informa o status e o valor ou motivo de cada um.
Devo usar promessas ou assíncrono/aguardar?
Eles trabalham juntos, pois a espera se baseia em promessas. A maior parte do código é melhor lida com async/await, enquanto Promise.all e métodos semelhantes permanecem úteis para trabalho paralelo.
As promessas acontecem em paralelo?
O JavaScript executa um trecho de código por vez em um thread, mas funciona como se as solicitações de rede acontecessem em segundo plano. Vários deles podem estar em andamento ao mesmo tempo.
Continue aprendendo
Precisa de ajuda com testes, WordPress ou SEO?
Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.
Mais guias sobre testes, IA e desenvolvimento moderno
Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.
Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las
Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…
Leia o artigo →DevOpsVariáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos
Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…
Leia o artigo →Desenvolvimento de softwareExplicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429
Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…
Leia o artigo →Automação de testesZombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem
Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…
Leia o artigo →Teste de softwareCobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa
Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…
Leia o artigo →Bancos de dados e SQLDELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um
DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…
Leia o artigo →Bancos de dados e SQLExibições SQL explicadas: crie, use e evite as armadilhas, com exemplos
Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…
Leia o artigo →Desenvolvimento de softwareInterface TypeScript vs tipo: diferenças reais, testadas com o compilador
Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…
Leia o artigo →Desenvolvimento de softwarePython *args e **kwargs explicados: funções flexíveis com exemplos claros
Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…
Leia o artigo →Desenvolvimento de softwareFunções Python Lambda: sintaxe, usos reais e quando não usá-los
Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …
Leia o artigo →Inteligência artificialO que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar
O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …
Leia o artigo →DevOpsMonitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos
Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…
Leia o artigo →DevOpsDocker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa
Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…
Leia o artigo →Automação de testesTDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um
TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…
Leia o artigo →Teste de softwareTeste de aceitação do usuário (UAT): processo, lista de verificação e exemplos reais
Explicação do teste de aceitação do usuário: quem o executa, como ele difere do controle de qualidade, um processo de sete etapas, um modelo de caso…
Leia o artigo →Bancos de dados e SQLValores SQL NULL explicados: IS NULL, COALESCE, NULLIF e as armadilhas a serem evitadas
Valores SQL NULL explicados com consultas que executamos: lógica de três valores, IS NULL, COALESCE, NULLIF, como agregações, junções e classificaçã…
Leia o artigo →Bancos de dados e SQLInstrução SQL CASE WHEN: sintaxe, exemplos reais e erros comuns
SQL CASE WHEN explicado com consultas que executamos: formulários simples e pesquisados, ordem de condição, pivôs com SUM, truques COUNT, classifica…
Leia o artigo →Desenvolvimento de softwareO que é JWT? JSON Web Tokens explicados com um exemplo prático
O que é JWT? JSON Web Tokens explicados: as três partes, um token construído e adulterado no código que executamos, JWT vs sessões, armazenamento, t…
Leia o artigo →Desenvolvimento de softwareLoop de eventos JavaScript explicado: pilha de chamadas, microtarefas e temporizadores com exemplos
O loop de eventos JavaScript explicado com o código que executamos: pilha de chamadas, filas de tarefas e microtarefas, por que setTimeout 0 não é i…
Leia o artigo →Desenvolvimento de softwareGeradores Python e rendimento explicado: economize memória com exemplos reais
Geradores Python e rendimento explicados com o código que executamos: como eles pausam e reiniciam, medem economia de memória, pipelines, sequências…
Leia o artigo →Inteligência artificialO que são incorporações? Similaridade de vetores e cossenos explicada de forma simples
O que são incorporações? Um guia em linguagem simples para vetores, similaridade de cossenos e pesquisa semântica, com um pequeno exemplo que execut…
Leia o artigo →DevOpsImplantação azul-verde vs canário: diferenças, compensações e como escolher
Explicação da implantação azul-verde versus canário: como cada um funciona, custo, risco e reversão, agrupamento estável de usuários que testamos, m…
Leia o artigo →Automação de testesTestes instáveis: o que os causa e como corrigi-los para sempre
Testes instáveis explicados com demonstrações que executamos: as principais causas, como confirmar e diagnosticar uma, soluções adequadas para tempo…
Leia o artigo →Teste de softwareTeste de carga versus teste de estresse: diferenças, métricas e exemplos k6
Teste de carga versus teste de estresse explicado: o que cada um prova, testes de pico e absorção, as métricas que importam, por que as médias engan…
Leia o artigo →Teste de softwareCaso de teste versus cenário de teste: principais diferenças, exemplos e quando usar cada um
Caso de teste vs cenário de teste explicado: definições claras, uma tabela lado a lado, um recurso de redefinição de senha retirado de cenários para…
Leia o artigo →Bancos de dados e SQLOtimização de consultas SQL: leia planos de execução e corrija consultas lentas, com intervalos
Otimização de consulta SQL testada em um milhão de linhas: leia planos de execução, adicione os índices corretos, corrija filtros não sargáveis, use…
Leia o artigo →Bancos de dados e SQLSQL UNION vs UNION ALL: diferenças, exemplos e desempenho testado
SQL UNION vs UNION ALL explicado com consultas que executamos: duplicatas, as regras que ambos seguem, um bug de dados real, desempenho medido, INTE…
Leia o artigo →Desenvolvimento de softwareTutorial da API JavaScript Fetch: GET, POST, tratamento de erros e tempos limite
Um tutorial da API JavaScript Fetch testado em um servidor local: GET e POST, por que 404 não lança, um auxiliar reutilizável, strings de consulta, …
Leia o artigo →Desenvolvimento de softwareManipulação de arquivos Python: leitura, gravação, CSV, JSON e pathlib com exemplos
Manipulação de arquivos Python explicada com código que executamos: modos abertos, com, leitura de arquivos grandes, CSV, JSON, pathlib, arquivos bi…
Leia o artigo →Desenvolvimento de softwareClasses e objetos Python: tutorial OOP com exemplos reais
Classes e objetos Python explicados com código que executamos: __init__, métodos, atributos de classe vs instância, herança, propriedades, métodos d…
Leia o artigo →Inteligência artificialO que é um modelo de linguagem grande? Como funcionam os LLMs, explicado sem exageros
O que é um modelo de linguagem grande? LLMs explicados em linguagem simples: tokens, incorporações, atenção, treinamento, temperatura e janelas de c…
Leia o artigo →DevOpsComandos Linux para iniciantes: o guia essencial do terminal com exemplos
Comandos Linux para iniciantes, agrupados por tarefa: navegação, arquivos, visualização de logs, grep, pipes, permissões, processos e arquivos, com …
Leia o artigo →Automação de testesSelenium vs Playwright: principais diferenças, código comparado e qual escolher
Selenium vs Playwright comparado: arquitetura, espera, suporte a navegador e linguagem, depuração, o mesmo teste em ambas as ferramentas, conselhos …
Leia o artigo →Teste de softwareParticionamento de equivalência: técnica de design de teste explicada com exemplos resolvidos
Explicação do particionamento de equivalência: como encontrar partições válidas e inválidas, exemplos resolvidos para idades, senhas e cupons, teste…
Leia o artigo →Teste de softwareGravidade vs Prioridade em Testes de Software: Diferenças, Matriz e Exemplos Reais
Explicação da gravidade versus prioridade em testes de software: definições claras, uma matriz de quatro vias com exemplos, escalas de nível, quem d…
Leia o artigo →Bancos de dados e SQLTutorial de subconsultas SQL: tipos, subconsultas correlacionadas e EXISTS com exemplos
Um tutorial de subconsultas SQL com consultas que executamos: subconsultas escalares, IN, FROM e SELECT, subconsultas correlacionadas, EXISTS, a arm…
Leia o artigo →Bancos de dados e SQLTransações SQL e ACID explicados: COMMIT, ROLLBACK e níveis de isolamento
Transações SQL e ACID explicadas com uma transferência bancária que executamos: COMMIT, ROLLBACK, savepoints, locks, níveis de isolamento, leituras …
Leia o artigo →Desenvolvimento de softwareFolha de dicas dos comandos do Git: os comandos que você realmente usa, com exemplos
Uma folha de dicas de comandos Git agrupada por tarefa: configuração, preparação, ramificações, controles remotos, stash, histórico e desfazer erros…
Leia o artigo →Desenvolvimento de softwareExplicação das promessas de JavaScript: então, catch, Promise.all e erros comuns
Promessas JavaScript explicadas com o código que executamos: estados, cadeias then e catch, tratamento de erros, Promise.all, allSettled, race e any…
Leia o artigo →Desenvolvimento de softwareTutorial do dicionário Python: métodos, exemplos e padrões comuns
Um tutorial de dicionário Python com código que executamos: criar, ler e atualizar dictos, métodos-chave, mesclagem, compreensão, padrões de contage…
Leia o artigo →Inteligência artificialO que é RAG? Geração aumentada de recuperação explicada com um exemplo prático
O que é RAG? Geração aumentada de recuperação explicada passo a passo: indexação, chunking, embeddings, recuperação, um pequeno exemplo funcional de…
Leia o artigo →DevOpsTutorial Terraform para iniciantes: infraestrutura como código do primeiro arquivo aos módulos
Um tutorial do Terraform para iniciantes: comece sem conta na nuvem, aprenda como iniciar, planejar e aplicar, depois variáveis, saídas, estado, mód…
Leia o artigo →DevOpsDocker vs Kubernetes: principais diferenças, como eles funcionam juntos e o que você precisa
Docker vs Kubernetes explicado: o que cada um faz, o mesmo aplicativo rodando com ambos, Docker Compose e Swarm comparados, a mudança do dockershim …
Leia o artigo →Automação de testesTutorial Pytest para iniciantes: luminárias, parametrização e exemplos reais
Um tutorial pytest para iniciantes com testes que executamos no pytest 9.2: asserções, fixtures, parametrizar, conftest, marcadores, tmp_path, monke…
Leia o artigo →Teste de softwareCiclo de vida de teste de software (STLC): 6 fases, critérios de entrada e saída, exemplo
O ciclo de vida de teste de software explicou: seis fases do STLC com critérios de entrada e saída, resultados, um exemplo de recurso de login funci…
Leia o artigo →Desenvolvimento de softwareGit merge vs rebase: diferenças, exemplos e quando usar cada um
Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.
Leia o artigo →Desenvolvimento de softwareList comprehension em Python: sintaxe, exemplos e quando evitar
List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.
Leia o artigo →Bancos de dados e SQLSQL JOIN explicado com exemplos (Inner, Left, Right, Full)
Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…
Leia o artigo →