then, catch, finally e encadeamento de promises
1. Fundamentos do Encadeamento com .then()
O encadeamento de promises é uma das características mais poderosas do JavaScript moderno. Cada chamada .then() retorna uma nova promise, permitindo criar cadeias de operações assíncronas sequenciais.
function buscarUsuario(id) {
return new Promise((resolve) => {
setTimeout(() => resolve({ id, nome: 'João' }), 100);
});
}
function buscarPedidos(usuario) {
return new Promise((resolve) => {
setTimeout(() => resolve(['Pedido 1', 'Pedido 2']), 100);
});
}
// Encadeamento correto - cada then retorna uma promise
buscarUsuario(1)
.then((usuario) => {
console.log('Usuário:', usuario.nome);
return buscarPedidos(usuario); // Retorna promise para próximo then
})
.then((pedidos) => {
console.log('Pedidos:', pedidos);
return pedidos.length;
})
.then((quantidade) => {
console.log('Total de pedidos:', quantidade);
});
Passagem de valores entre .then() consecutivos:
// O valor retornado no primeiro then é passado para o segundo
Promise.resolve(10)
.then((valor) => valor * 2) // Retorna 20
.then((valor) => valor + 5) // Recebe 20, retorna 25
.then((valor) => console.log(valor)); // 25
Evite Promises aninhadas - prefira encadeamento plano:
// ❌ Errado: Promise Hell
buscarUsuario(1)
.then((usuario) => {
buscarPedidos(usuario).then((pedidos) => {
console.log(pedidos);
});
});
// ✅ Correto: Encadeamento plano
buscarUsuario(1)
.then(buscarPedidos)
.then(console.log);
2. Captura de Erros com .catch()
O .catch() captura qualquer rejeição que ocorra na cadeia anterior a ele.
function operacaoRisco(deveFalhar) {
return new Promise((resolve, reject) => {
if (deveFalhar) {
reject(new Error('Operação falhou!'));
} else {
resolve('Sucesso!');
}
});
}
// Erro em qualquer ponto da cadeia é capturado
operacaoRisco(false)
.then((resultado) => {
console.log('Primeiro then:', resultado);
throw new Error('Erro inesperado no primeiro then');
})
.then(() => {
console.log('Este then nunca executa');
})
.catch((erro) => {
console.error('Capturado:', erro.message);
});
Posicionamento estratégico do .catch():
// Catch no meio da cadeia - permite recuperação
buscarUsuario(1)
.then((usuario) => {
return buscarPedidos(usuario).catch((erro) => {
console.error('Erro ao buscar pedidos:', erro);
return []; // Valor padrão para continuar a cadeia
});
})
.then((pedidos) => {
console.log('Pedidos (ou array vazio):', pedidos);
});
3. Execução Garantida com .finally()
O .finally() executa independentemente da promise ser resolvida ou rejeitada. É ideal para limpeza de recursos.
function conectarBanco() {
let conexaoAberta = true;
return new Promise((resolve, reject) => {
setTimeout(() => {
// Simula sucesso ou falha
if (Math.random() > 0.5) {
resolve('Dados do banco');
} else {
reject(new Error('Falha na conexão'));
}
}, 100);
}).finally(() => {
// Sempre executa, independente do resultado
conexaoAberta = false;
console.log('Conexão fechada');
});
}
conectarBanco()
.then((dados) => console.log('Dados:', dados))
.catch((erro) => console.error('Erro:', erro.message));
.finally() não recebe o valor resolvido nem o erro:
Promise.resolve('Valor secreto')
.finally((valor) => {
console.log('Finally não recebe:', valor); // undefined
})
.then((valor) => {
console.log('Then recebe:', valor); // 'Valor secreto'
});
4. Padrões de Encadeamento em Node.js
Leitura sequencial de arquivos com fs.promises:
const fs = require('fs').promises;
fs.readFile('config.json', 'utf8')
.then((configData) => {
const config = JSON.parse(configData);
return fs.readFile(config.templateFile, 'utf8');
})
.then((template) => {
console.log('Template carregado:', template.substring(0, 50));
return fs.writeFile('output.txt', template);
})
.then(() => {
console.log('Arquivo salvo com sucesso');
})
.catch((erro) => {
console.error('Erro no pipeline:', erro.message);
});
Pipeline de transformação de dados:
const fetch = require('node-fetch');
fetch('https://api.exemplo.com/usuarios')
.then((resposta) => {
if (!resposta.ok) throw new Error('Falha na requisição');
return resposta.json();
})
.then((dados) => {
// Processa dados
return dados.filter((user) => user.ativo);
})
.then((usuariosAtivos) => {
// Salva no banco
return salvarNoBanco(usuariosAtivos);
})
.then(() => {
console.log('Pipeline concluído');
})
.catch((erro) => {
console.error('Pipeline falhou:', erro);
});
5. Encadeamento em React com Hooks
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
setError(null);
fetch(`https://api.exemplo.com/users/${userId}`)
.then((response) => {
if (!response.ok) throw new Error('Usuário não encontrado');
return response.json();
})
.then((data) => {
setUser(data);
})
.catch((err) => {
setError(err.message);
})
.finally(() => {
setLoading(false); // Garantido executar
});
// Cleanup function
return () => {
// Limpeza se necessário
};
}, [userId]);
if (loading) return <div>Carregando...</div>;
if (error) return <div>Erro: {error}</div>;
return <div>{user?.name}</div>;
}
6. Combinação de .catch() e .finally() em Cadeias Complexas
function processarPagamento(valor) {
return new Promise((resolve, reject) => {
if (valor <= 0) reject(new Error('Valor inválido'));
else resolve({ status: 'aprovado', valor });
});
}
processarPagamento(100)
.then((pagamento) => {
console.log('Pagamento aprovado:', pagamento.valor);
return debitarConta(pagamento.valor);
})
.catch((erro) => {
if (erro.message === 'Valor inválido') {
console.error('Erro de validação:', erro.message);
return Promise.reject(erro); // Propaga para próximo catch
}
console.error('Erro no débito:', erro.message);
return reembolsar(pagamento.valor);
})
.catch((erro) => {
console.error('Erro crítico:', erro.message);
})
.finally(() => {
console.log('Processo finalizado');
fecharConexao();
});
7. Boas Práticas e Armadilhas Comuns
❌ Não esquecer de retornar a Promise dentro de .then():
// ❌ Errado - não retorna a promise
buscarUsuario(1)
.then((usuario) => {
buscarPedidos(usuario); // Promise não retornada!
})
.then((pedidos) => {
console.log(pedidos); // undefined
});
// ✅ Correto
buscarUsuario(1)
.then((usuario) => buscarPedidos(usuario)) // Retorna a promise
.then((pedidos) => console.log(pedidos));
Usar Promise.resolve() e Promise.reject() para iniciar cadeias:
Promise.resolve()
.then(() => validarDados(input))
.then((dados) => processarDados(dados))
.then((resultado) => salvarResultado(resultado))
.catch((erro) => console.error(erro));
Promise.reject(new Error('Iniciar com erro'))
.catch((erro) => {
console.log('Recuperando do erro:', erro.message);
return 'Valor de fallback';
})
.then((valor) => console.log('Continuou com:', valor));
Armadilha: Erros não capturados em Promises:
// ❌ Erro não capturado - Promise sem catch
new Promise(() => {
throw new Error('Erro silencioso');
});
// ✅ Sempre adicione um catch final
new Promise(() => {
throw new Error('Erro capturado');
}).catch((erro) => console.error(erro.message));
Referências
- MDN Web Docs: Promise.prototype.then() — Documentação oficial sobre o método then() com exemplos detalhados de encadeamento
- MDN Web Docs: Promise.prototype.catch() — Guia completo sobre captura de erros em promises
- MDN Web Docs: Promise.prototype.finally() — Documentação oficial do método finally() e seus casos de uso
- Node.js Documentation: fs.promises API — Referência oficial da API de promises do módulo fs do Node.js
- React Documentation: Using Effects — Guia oficial sobre useEffect e boas práticas com promises em React
- JavaScript.info: Promises Chaining — Tutorial completo sobre encadeamento de promises com exemplos práticos
- Google Web Dev: Promises Patterns — Padrões e anti-padrões no uso de promises em JavaScript