Promises em JavaScript
Aprenda Promises em JavaScript: estados pending, fulfilled e rejected, a função executora com resolve e reject, then/catch/finally e microtarefas, com exemplos executáveis.
As Promises em JavaScript são uma ferramenta poderosa para gerenciar operações assíncronas, permitindo que os desenvolvedores escrevam código mais limpo e robusto. Este capítulo explica o que é uma promise, os três estados em que ela pode estar, como a função executora funciona com resolve e reject, como consumir resultados com .then, .catch e .finally, e quando os callbacks de promise realmente são executados (a fila de microtarefas). Compreender esses fundamentos é essencial antes de avançar para encadeamento, a Promise API e async/await.
Introdução às Promises em JavaScript
Uma Promise é um objeto que representa um valor que pode não estar disponível ainda, mas estará em algum momento no futuro. Em vez de passar um callback para uma função assíncrona e torcer para que ele seja chamado, você recebe um objeto promise imediatamente e anexa callbacks a ele. Isso evita callbacks profundamente aninhados, frequentemente chamados de "callback hell", e oferece uma maneira única e consistente de lidar com sucesso e falha.
Uma tarefa assíncrona típica — uma requisição de rede, um timer, a leitura de um arquivo — não tem seu resultado pronto imediatamente. A promise é um placeholder para esse resultado.
Os Três Estados de uma Promise
Uma promise está sempre em exatamente um dos três estados:
- pending — o estado inicial; a operação ainda não foi concluída.
- fulfilled — a operação foi concluída com sucesso e a promise tem um valor.
- rejected — a operação falhou e a promise tem um motivo (geralmente um
Error).
Uma promise pendente pode fazer a transição para fulfilled ou rejected. Uma vez que isso ocorra, ela está settled e nunca mais pode mudar de estado. Essa transição única e unidirecional é o que torna as promises previsíveis: um callback .then anexado a uma promise já cumprida ainda será executado, e uma promise nunca pode voltar do estado fulfilled para rejected.
┌─────────────┐ resolve(value) ┌─────────────┐
│ pending │ ────────────────▶ │ fulfilled │
new Promise ─▶ │ │ └─────────────┘
│ │ reject(reason) ┌─────────────┐
└─────────────┘ ────────────────▶ │ rejected │
└─────────────┘Criando uma Promise
Para criar uma promise, você chama o construtor Promise e passa uma função chamada de executor. O executor é executado imediata e sincronicamente no momento em que a promise é criada. Ele recebe duas funções como argumentos, convencionalmente chamadas de resolve e reject:
- Chame
resolve(value)para cumprir a promise comvalue. - Chame
reject(reason)para rejeitar a promise comreason.
Até que você chame uma delas, a promise permanece pending.
O executor recebe resolve e reject para que possa liquidar a promise assim que o trabalho assíncrono terminar. Aqui a promise é cumprida após um timer de um segundo:
Nota: Apenas a primeira chamada a
resolveourejectimporta. Uma vez que a promise está settled, quaisquer chamadas adicionais aresolveourejectsão ignoradas. Além disso, se o executor lançar um erro de forma síncrona, a promise é automaticamente rejeitada com esse erro.
Tratando Resultados com .then, .catch e .finally
Uma vez que uma promise foi criada, você consome seu resultado com os métodos .then, .catch e .finally. É assim que o restante do seu código reage a uma promise settled.
O método then
O método .then é usado para agendar um callback a ser executado quando a promise é cumprida. Para que uma promise seja cumprida, o método resolve deve ser chamado. O argumento que você passa para o método resolve será o valor final da promise.
Neste código, a promise só será cumprida após o timeout de 1000 ms, e o método resolve é chamado com "Done!".
A função na parte then só é executada após o método resolve ser chamado.
.then também pode receber um segundo argumento — um handler de rejeição — mas usar um .catch separado (abaixo) é mais claro e captura erros de handlers anteriores também.
O método .catch
O método .catch é usado para tratar a promise caso ela seja rejeitada. Isso significa que um erro foi lançado no bloco da função da promise ou que o método reject foi chamado.
Para padrões de tratamento de erros mais avançados — rejeição versus erros lançados, relançamento e recuperação dentro de uma cadeia — consulte Tratamento de Erros com Promises.
O método .finally
O método .finally permite executar código após a promise estar settled, independentemente do resultado. Ele não recebe argumentos (não sabe se a promise foi cumprida ou rejeitada) e passa o resultado ou erro sem alterações, sendo ideal para limpeza, como ocultar um indicador de carregamento.
Quando os Callbacks de Promise São Executados? (Microtarefas)
Uma surpresa comum é que os callbacks de .then, .catch e .finally nunca são executados de forma síncrona, mesmo que a promise já esteja settled. Eles são agendados na fila de microtarefas, que o motor processa somente após o código síncrono atual terminar.
Isso significa que o código síncrono sempre é executado primeiro, e os callbacks de promise são executados antes dos timers (setTimeout), que ficam na fila de macrotarefas separada.
Mesmo que a promise seja resolvida imediatamente e o timeout seja 0, o callback da promise (3) é executado antes do callback do timeout (4), porque a fila de microtarefas é completamente esvaziada antes da próxima macrotarefa ser executada.
Buscando Dados de uma API usando Promises
Este exemplo mostra como buscar dados de uma API remota usando promises.
Encadeamento de Promises
O encadeamento de promises é um recurso poderoso que permite vincular várias operações assíncronas. Cada .then retorna uma nova promise, e o que você return de um handler se torna o valor de cumprimento dessa nova promise — é por isso que o exemplo abaixo carrega um valor por várias etapas. Para mais informações, consulte JavaScript: Promises e Encadeamento.
Integrando async/await com Promises em JavaScript
Usar async/await de forma eficaz pode simplificar o tratamento de operações assíncronas, tornando seu código mais limpo e fácil de entender, mantendo todo o poder das promises JavaScript. Você aprenderá mais sobre isso em JavaScript async/await, mas aqui está um exemplo simples.
Conclusão
Dominar as promises JavaScript é fundamental para qualquer desenvolvedor que queira gerenciar operações assíncronas com eficiência. Lembre-se do modelo central: uma promise está pending até que o executor chame resolve ou reject, após o que ela está settled para sempre; você lê o resultado com .then/.catch/.finally; e esses callbacks sempre são executados como microtarefas, após o código síncrono atual.
Próximos passos
- JavaScript: Promises e Encadeamento — sequencie etapas assíncronas e passe valores adiante.
- A Promise API — helpers estáticos como
Promise.all,Promise.race,Promise.allSettledePromise.resolve. - Tratamento de Erros com Promises — rejeições, erros lançados e padrões de recuperação.
- JavaScript async/await — escreva código baseado em promises que parece código síncrono.