No artigo anterior vimos o Callback Hell — código que cresce horizontalmente, difícil de ler e manter. As Promises foram introduzidas no ES6 (2015) como a solução oficial do JavaScript para esse problema.
Uma Promise representa um valor que ainda não está disponível, mas que estará em algum momento no futuro — ou que pode falhar. Em vez de passar um callback para dentro de uma função, você recebe um objeto que representa a operação em andamento e encadeia o que fazer com o resultado.
O conceito
Pense em uma Promise como um recibo de pedido num restaurante. Você faz o pedido (inicia a operação), recebe um número (a Promise), e continua fazendo outras coisas. Quando o pedido fica pronto, o garçom te avisa (resolve). Se algo deu errado na cozinha, você também é avisado (reject).
// Uma Promise tem sempre um de três estados:
// pending → aguardando (operação em andamento)
// fulfilled → concluída com sucesso (resolve foi chamado)
// rejected → falhou (reject foi chamado)
Criando uma Promise
const minhaPromise = new Promise(function(resolve, reject) {
// O executor — executa imediatamente
const sucesso = true;
if (sucesso) {
resolve("Operação concluída com sucesso!"); // fulfills a promise
} else {
reject(new Error("Algo deu errado.")); // rejects a promise
}
});
O construtor recebe uma função chamada executor, que recebe dois callbacks:
resolve(valor)— chame quando a operação tiver sucessoreject(erro)— chame quando a operação falhar
Consumindo com .then() e .catch()
minhaPromise
.then(function(resultado) {
// Executado se a promise foi resolvida
console.log(resultado); // "Operação concluída com sucesso!"
})
.catch(function(erro) {
// Executado se a promise foi rejeitada
console.error(erro.message);
});
Com arrow functions — como você verá na prática:
minhaPromise
.then(resultado => console.log(resultado))
.catch(erro => console.error(erro.message));
Promise assíncrona — o caso real
function buscarUsuario(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id <= 0) {
reject(new Error("ID inválido."));
return;
}
resolve({
id,
nome: "Ana Paula",
email: "ana@email.com",
plano: "premium",
});
}, 1000);
});
}
// Usando a função
buscarUsuario(42)
.then(usuario => {
console.log(`Olá, ${usuario.nome}!`);
console.log(`Plano: ${usuario.plano}`);
})
.catch(erro => {
console.error(`Erro: ${erro.message}`);
});
buscarUsuario(-1)
.then(usuario => console.log(usuario.nome))
.catch(erro => console.error(`Erro: ${erro.message}`)); // Erro: ID inválido.
Compare com a versão em callback do artigo anterior — muito mais limpo.
Encadeamento de .then()
A grande vantagem das Promises: você pode encadear operações dependentes de forma linear, sem aninhar:
function buscarUsuario(id) {
return new Promise(resolve => {
setTimeout(() => resolve({ id, nome: "Carlos", enderecoId: 7 }), 500);
});
}
function buscarEndereco(enderecoId) {
return new Promise(resolve => {
setTimeout(() => resolve({ id: enderecoId, cidade: "Curitiba", rua: "Av. Brasil" }), 400);
});
}
function buscarPedidos(usuarioId) {
return new Promise(resolve => {
setTimeout(() => resolve([{ id: 101, total: 250 }, { id: 102, total: 180 }]), 600);
});
}
// Encadeamento linear — sem pirâmide!
buscarUsuario(1)
.then(usuario => {
console.log(`Usuário: ${usuario.nome}`);
return buscarEndereco(usuario.enderecoId); // retorna uma nova Promise
})
.then(endereco => {
console.log(`Cidade: ${endereco.cidade}`);
return buscarPedidos(1); // retorna uma nova Promise
})
.then(pedidos => {
console.log(`Pedidos: ${pedidos.length}`);
const total = pedidos.reduce((acc, p) => acc + p.total, 0);
console.log(`Total: R$ ${total}`);
})
.catch(erro => {
// Um único catch trata erros de QUALQUER etapa da cadeia
console.error(`Algo deu errado: ${erro.message}`);
});
Antes eram 4 níveis aninhados de callbacks. Agora é uma cadeia linear e legível. O catch no final captura qualquer erro de qualquer etapa.
Como o encadeamento funciona
Cada .then() retorna uma nova Promise. O valor retornado dentro do .then() se torna o valor resolvido da próxima Promise na cadeia:
Promise.resolve(1)
.then(valor => {
console.log(valor); // 1
return valor + 1; // retorna 2
})
.then(valor => {
console.log(valor); // 2
return valor * 3; // retorna 6
})
.then(valor => {
console.log(valor); // 6
});
Se você retornar uma Promise dentro do .then(), o encadeamento espera essa Promise resolver antes de chamar o próximo .then().
.finally() — executar sempre
Assim como o finally do try/catch, ele executa independente de sucesso ou falha:
function carregarDados() {
mostrarLoading(true);
return buscarUsuario(1)
.then(usuario => {
exibirUsuario(usuario);
})
.catch(erro => {
exibirErro(erro.message);
})
.finally(() => {
mostrarLoading(false); // sempre oculta o loading
});
}
Promise.resolve() e Promise.reject()
Atalhos para criar Promises já resolvidas ou rejeitadas:
// Já resolvida
Promise.resolve("valor imediato")
.then(v => console.log(v)); // "valor imediato"
// Já rejeitada
Promise.reject(new Error("falha imediata"))
.catch(e => console.error(e.message)); // "falha imediata"
// Útil para tornar funções síncronas compatíveis com código assíncrono
function obterConfiguracao(chave) {
const cache = { tema: "escuro", idioma: "pt-BR" };
if (cache[chave]) {
return Promise.resolve(cache[chave]); // retorna Promise mesmo sendo síncrono
}
return buscarConfiguracaoDoServidor(chave); // assíncrono real
}
Promise.all() — executar em paralelo
Executa múltiplas Promises ao mesmo tempo e aguarda todas terminarem:
const promessas = [
buscarUsuario(1),
buscarUsuario(2),
buscarUsuario(3),
];
Promise.all(promessas)
.then(usuarios => {
// usuarios é um array com os resultados na mesma ordem
console.log(`${usuarios.length} usuários carregados`);
usuarios.forEach(u => console.log(`- ${u.nome}`));
})
.catch(erro => {
// Se QUALQUER uma falhar, cai aqui
console.error(`Falha: ${erro.message}`);
});
Promise.all é essencial para performance — em vez de fazer 3 requisições em sequência (3 segundos), faz as 3 ao mesmo tempo (~1 segundo, o tempo da mais lenta).
Promise.allSettled() — quando quer todos os resultados
Diferente do Promise.all, o allSettled não falha se uma Promise rejeitar — retorna o resultado de todas, sejam sucessos ou falhas:
const promises = [
buscarUsuario(1), // vai funcionar
buscarUsuario(-1), // vai rejeitar
buscarUsuario(3), // vai funcionar
];
Promise.allSettled(promises)
.then(resultados => {
resultados.forEach((resultado, i) => {
if (resultado.status === "fulfilled") {
console.log(`✅ Usuário ${i + 1}: ${resultado.value.nome}`);
} else {
console.log(`❌ Usuário ${i + 1}: ${resultado.reason.message}`);
}
});
});
// ✅ Usuário 1: Ana Paula
// ❌ Usuário 2: ID inválido.
// ✅ Usuário 3: Ana Paula
Promise.race() — o mais rápido vence
Resolve ou rejeita assim que a primeira Promise terminar:
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout: requisição muito lenta.")), 3000)
);
const requisicao = buscarUsuario(1);
Promise.race([requisicao, timeout])
.then(usuario => console.log(`Carregado: ${usuario.nome}`))
.catch(erro => console.error(erro.message));
// Se buscarUsuario demorar mais de 3s → "Timeout: requisição muito lenta."
// Se terminar antes → exibe o usuário
Isso é um padrão muito útil para implementar timeout em requisições.
Promise.any() — o primeiro sucesso
Resolve com o primeiro que tiver sucesso. Só rejeita se todos falharem:
// Tentar múltiplos servidores — usa o que responder primeiro
const servidores = [
buscarDoServidor("servidor-1"),
buscarDoServidor("servidor-2"),
buscarDoServidor("servidor-3"),
];
Promise.any(servidores)
.then(dados => console.log("Dados recebidos:", dados))
.catch(erro => console.error("Todos os servidores falharam."));
Tratamento de erros em Promises
// ✅ Um catch no final trata toda a cadeia
buscarUsuario(1)
.then(usuario => buscarPedidos(usuario.id))
.then(pedidos => processarPedidos(pedidos))
.catch(erro => console.error(erro.message));
// ✅ Catch intermediário — recupera e continua
buscarConfiguracoes()
.catch(erro => {
console.warn("Configurações não encontradas. Usando padrões.");
return { tema: "claro", idioma: "pt-BR" }; // valor de fallback
})
.then(config => {
// config é o valor real OU o fallback
aplicarConfiguracoes(config);
});
// ⚠️ Promessa rejeitada sem catch = UnhandledPromiseRejection
// Sempre adicione .catch() nas suas Promises
buscarUsuario(-1); // ⚠️ sem catch — aviso no console
buscarUsuario(-1).catch(e => console.error(e)); // ✅
Exemplo completo — Dashboard de dados
// Funções que retornam Promises (simulando APIs)
function buscarEstatisticas() {
return new Promise(resolve =>
setTimeout(() => resolve({
usuarios: 1284,
pedidos: 342,
receita: 48750.90,
crescimento: 12.4,
}), 800)
);
}
function buscarUltimosPedidos() {
return new Promise(resolve =>
setTimeout(() => resolve([
{ id: 1001, cliente: "Ana", total: 250, status: "entregue" },
{ id: 1002, cliente: "Bruno", total: 180, status: "enviado" },
{ id: 1003, cliente: "Clara", total: 95, status: "pendente" },
]), 600)
);
}
function buscarAlertas() {
return new Promise(resolve =>
setTimeout(() => resolve([
{ tipo: "aviso", mensagem: "Estoque baixo: Produto #42" },
{ tipo: "info", mensagem: "3 novos usuários hoje" },
]), 400)
);
}
function buscarMetasMes() {
return new Promise(resolve =>
setTimeout(() => resolve({
meta: 50000,
atual: 48750.90,
porcentagem: 97.5,
}), 700)
);
}
// Carrega tudo em paralelo — eficiente!
function carregarDashboard() {
console.log("⏳ Carregando dashboard...");
const inicio = Date.now();
Promise.all([
buscarEstatisticas(),
buscarUltimosPedidos(),
buscarAlertas(),
buscarMetasMes(),
])
.then(([stats, pedidos, alertas, metas]) => {
const tempo = Date.now() - inicio;
console.log(`✅ Dashboard carregado em ${tempo}ms\n`);
// Estatísticas
console.log("📊 ESTATÍSTICAS");
console.log(` Usuários: ${stats.usuarios.toLocaleString("pt-BR")}`);
console.log(` Pedidos: ${stats.pedidos}`);
console.log(` Receita: R$ ${stats.receita.toLocaleString("pt-BR", { minimumFractionDigits: 2 })}`);
console.log(` Crescimento: +${stats.crescimento}%\n`);
// Meta do mês
console.log("🎯 META DO MÊS");
const barraSize = 20;
const preenchido = Math.round((metas.porcentagem / 100) * barraSize);
const barra = "█".repeat(preenchido) + "░".repeat(barraSize - preenchido);
console.log(` [${barra}] ${metas.porcentagem}%`);
console.log(` R$ ${metas.atual.toLocaleString("pt-BR")} de R$ ${metas.meta.toLocaleString("pt-BR")}\n`);
// Últimos pedidos
console.log("🛍️ ÚLTIMOS PEDIDOS");
pedidos.forEach(p => {
const icone = { entregue: "✅", enviado: "📦", pendente: "⏳" }[p.status];
console.log(` ${icone} #${p.id} — ${p.cliente}: R$ ${p.total}`);
});
// Alertas
if (alertas.length > 0) {
console.log("\n🔔 ALERTAS");
alertas.forEach(a => {
const icone = a.tipo === "aviso" ? "⚠️" : "ℹ️";
console.log(` ${icone} ${a.mensagem}`);
});
}
})
.catch(erro => {
console.error(`❌ Erro ao carregar dashboard: ${erro.message}`);
})
.finally(() => {
console.log("\n── Fim do carregamento ──");
});
}
carregarDashboard();
Comparativo: Callbacks vs Promises
// ── Callbacks ──────────────────────────────
buscarUsuario(1, function(err, usuario) {
if (err) return tratarErro(err);
buscarPedidos(usuario.id, function(err, pedidos) {
if (err) return tratarErro(err);
processarPedidos(pedidos, function(err, resultado) {
if (err) return tratarErro(err);
console.log(resultado);
});
});
});
// ── Promises ───────────────────────────────
buscarUsuario(1)
.then(usuario => buscarPedidos(usuario.id))
.then(pedidos => processarPedidos(pedidos))
.then(resultado => console.log(resultado))
.catch(erro => tratarErro(erro));
A diferença é clara. E ainda vai melhorar no próximo artigo, quando chegarmos ao async/await.
Tarefa para você
Implemente um sistema de checkout de e-commerce com Promises:
// Implemente as funções abaixo retornando Promises com setTimeout:
// validarCarrinho(itens)
// → resolve com itens se válido
// → rejeita se carrinho estiver vazio
// verificarEstoque(itens)
// → resolve com itens se todos têm estoque
// → rejeita com qual produto está sem estoque
// calcularFrete(cep)
// → resolve com { valor, prazo }
// → rejeita se CEP inválido (menos de 8 dígitos)
// processarPagamento(total, cartao)
// → resolve com { aprovado: true, codigo }
// → rejeita se cartao.limite < total
// confirmarPedido(dados)
// → resolve com { numeroPedido, previsaoEntrega }
// Monte a cadeia com Promise.all onde possível
// (verificarEstoque e calcularFrete podem ser paralelos)
// Trate cada tipo de erro com mensagem específica
Conclusão
Neste artigo você aprendeu:
- O que é uma Promise e seus três estados
- Como criar Promises com
new Promise(resolve, reject) - Consumir com
.then(),.catch()e.finally() - Encadear operações de forma linear e legível
Promise.allpara operações em paraleloPromise.allSettledquando quer todos os resultadosPromise.racepara timeout e corridaPromise.anypara o primeiro sucesso- Tratamento de erros e fallbacks
- Como Promises resolvem o Callback Hell
📚 Fontes e Referências
- MDN Web Docs — Promise: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise
- MDN Web Docs — Promise.all: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
- MDN Web Docs — Promise.allSettled: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
- MDN Web Docs — Using Promises: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Using_promises
- JavaScript.info — Promises: https://javascript.info/promise-basics
- JavaScript.info — Promise chaining: https://javascript.info/promise-chaining
- JavaScript.info — Promise API: https://javascript.info/promise-api
- You Don't Know JS: Async & Performance, Cap. 3 — Kyle Simpson: https://github.com/getify/You-Dont-Know-JS
- Eloquent JavaScript, Cap. 11 — Asynchronous Programming: https://eloquentjavascript.net/11_async.html