W3docs

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() recebe undefined.
  • 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:

javascript— editable

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:

javascript— editable

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:

javascript— editable

Adicionar return reconecta a cadeia. Agora o segundo .then() aguarda a consulta dos posts e recebe seu resultado:

javascript— editable

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():

javascript— editable

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:

javascript— editable

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.

javascript— editable

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.

javascript— editable

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 return faltando dentro de .then() — ele desconecta o trabalho assíncrono interno e a próxima etapa recebe undefined.
  • 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.all para 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.

Prática

Prática
Qual é o propósito do Encadeamento de Promises em JavaScript?
Qual é o propósito do Encadeamento de Promises em JavaScript?
Was this page helpful?