Encadeamento de Promises em JavaScript
Aprenda como o encadeamento de promises em JavaScript permite executar operações assíncronas em sequência, com tratamento de erros e cleanup eficientes.
O encadeamento de promises permite executar operações assíncronas uma após a outra, onde cada etapa começa somente após a anterior terminar. Você anexa uma sequência de handlers .then() a uma promise, e cada handler recebe o resultado da etapa anterior.
Antes das promises, sequenciar trabalho assíncrono significava aninhar callbacks dentro de callbacks — o famoso "callback hell" ou "pirâmide da perdição":
queryDatabase('users', (users) => {
queryDatabase('posts', (posts) => {
queryDatabase('comments', (comments) => {
// deeply nested, hard to read, error handling duplicated everywhere
});
});
});O encadeamento achata essa pirâmide em uma sequência legível, de cima para baixo, com um único lugar para tratar erros. Esta página explica como o encadeamento realmente funciona, o erro mais comum (um return esquecido), recuperação de erros e cleanup. Para a sintaxe relacionada que se baseia nas promises, veja JavaScript: async/await.
Como o Encadeamento Funciona: Cada .then() Retorna uma Nova Promise
Este é o mecanismo central, e todo o resto decorre dele. .then() não retorna a promise original — ele retorna uma promise completamente nova. O que essa nova promise resolve depende do que o seu handler retorna:
- Retornar um valor simples → o próximo
.then()recebe esse valor. - Retornar uma promise → a cadeia aguarda ela ser resolvida, e o próximo
.then()recebe seu valor resolvido (não a promise em si). - Não retornar nada → o próximo
.then()recebeundefined. - Lançar um erro → a cadeia pula para o
.catch()mais próximo.
Como cada .then() retorna uma nova promise, você pode continuar anexando chamadas .then() e passar um valor adiante:
O caso poderoso é retornar uma promise de um handler. A cadeia é pausada até que essa promise seja resolvida antes de continuar, o que é exatamente como você sequencia operações assíncronas dependentes:
Encadeamento Básico de Promises
Considere o cenário em que você precisa consultar um banco de dados e, em seguida, usar o resultado dessa consulta para fazer outra. Cada .then() retorna a promise da próxima consulta, de modo que a cadeia aguarda o término de uma consulta antes de iniciar a próxima:
O Bug #1: Esquecer o return (a "cadeia desconectada")
Este é o erro mais comum no encadeamento de promises. Se você inicia uma operação assíncrona dentro de um .then() mas esquece de retornar sua promise, a cadeia não aguarda por ela — o resultado é perdido e o próximo .then() é executado imediatamente com undefined. A promise interna se torna uma cadeia "desconectada" que executa por conta própria.
Na versão quebrada abaixo, queryDatabase('posts') é chamada, mas sua promise não é retornada, então o segundo .then() registra undefined em vez dos posts:
Adicionar return reconecta a cadeia. Agora o segundo .then() aguarda a consulta dos posts e recebe seu resultado:
Dica: arrow functions com corpo de expressão retornam automaticamente —
.then(r => queryDatabase(r))retorna a promise, mas.then(r => { queryDatabase(r); })(com chaves) não retorna.
Tratamento de Erros em Cadeias
Um único .catch() no final da cadeia trata qualquer erro lançado — ou qualquer promise rejeitada — em qualquer etapa anterior. Quando algo falha, a cadeia pula todos os .then() restantes e vai direto para o próximo .catch().
Neste exemplo, a primeira consulta é rejeitada, então o .then() é completamente ignorado e o controle vai para o .catch():
Para uma análise mais aprofundada dos padrões de rejeição, veja Tratamento de Erros com Promises.
.catch() no meio da cadeia para recuperação
Um .catch() não precisa ser o último elo. Colocado no meio de uma cadeia, ele pode tratar um erro, retornar um valor de fallback e deixar a cadeia continuar. Esta é a diferença entre se recuperar de uma falha e abortar toda a sequência.
Abaixo, a primeira etapa falha, mas um .catch() no meio da cadeia fornece um valor padrão e a cadeia segue adiante:
Um .catch() no meio da cadeia recupera e retoma; um .catch() terminal é a rede de segurança final para tudo que não foi recuperado antes.
Cleanup com .finally()
.finally() é executado assim que a promise é resolvida ou rejeitada. Ele não recebe nenhum argumento e não altera o valor que passa pela cadeia, o que o torna ideal para cleanup que deve acontecer independentemente do resultado: ocultar um spinner, fechar uma conexão ou reativar um botão.
Executando Promises em Paralelo: Promise.all
Promise.all não é encadeamento — encadeamento é sequencial (um após o outro), enquanto Promise.all executa promises em paralelo e aguarda todas elas. Use-o quando as operações não dependem umas das outras, portanto não há razão para aguardar uma antes de iniciar a próxima.
Ele recebe um iterável de promises e retorna uma única promise que resolve para um array com seus resultados, na mesma ordem da entrada. Qualquer valor que não seja uma promise no array (como 42 abaixo) é automaticamente envolvido em uma promise resolvida. Se qualquer entrada for rejeitada, o Promise.all inteiro é rejeitado imediatamente com esse erro.
Para Promise.all, Promise.race, Promise.allSettled e os demais combinadores, veja a API de Promise.
Resumo
- Cada
.then()retorna uma promise nova; a cadeia é lida de cima para baixo em vez de aninhada. - Retorne valores para passá-los adiante, e retorne uma promise de um handler para fazer a cadeia aguardar por ela.
- O erro mais comum é um
returnfaltando dentro de.then()— ele desconecta o trabalho assíncrono interno e a próxima etapa recebeundefined. - Um
.catch()terminal trata erros de qualquer etapa anterior; um.catch()no meio da cadeia pode recuperar e retomar. - Use
.finally()para cleanup que deve ser executado independentemente de a cadeia ter sido bem-sucedida ou falhado. - Use
Promise.allpara trabalho independente que deve ser executado em paralelo — essa é uma ferramenta diferente do encadeamento sequencial. - Quando você estiver confortável aqui, async/await oferece o mesmo comportamento com uma sintaxe de aparência síncrona.